Introduction & Set up
Chicago’s crime rates have long been a subject of national and
international concern and the city itself serves as a microcosm of urban
crime challenges faced by major metropolitan areas worldwide. By
examining the patterns and determinants of robbery in Chicago, the
dynamics of criminal activities can be understood better, which provides
valuable insights into broader issues such as social inequality,
policing strategies, and community safety.
Selection bias happen when places that have suffered from robbery
incidents historically are likely to be predicted to have higher robbery
rates than usual. This may due to complicated reasons. Using a dataset
for specific year for analysis can introduce bias, as robbery patterns
can vary over time. If the chosen period doesn’t capture these
variations, the study results may not be generalizable. Other factors
like the survivorship bias in data recording may also result in
selection bias in modeling. The bias can lead to an underestimation or
overestimation of the risk of robbery in certain areas, which can lead
to an incomplete and potentially skewed understanding of the factors
contributing to robbery and its spatial or temporal patterns in Chicago.
Furthermore, public policy and law enforcement strategies may be
misinformed if selection bias leads to inaccurate insights into
high-risk areas or demographics.
\(All data resource from Chicago Open Data
site (https://data.cityofchicago.org/).\)
knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE
)
library(tidyverse)
library(sf)
library(RSocrata)
library(viridis)
library(spatstat.explore)
library(raster)
library(spdep)
library(FNN)
library(grid)
library(gridExtra)
library(knitr)
library(kableExtra)
library(tidycensus)
library(classInt) # for KDE and ML risk class intervals
# functions
root.dir = "https://raw.githubusercontent.com/urbanSpatial/Public-Policy-Analytics-Landing/master/DATA/"
source("https://raw.githubusercontent.com/urbanSpatial/Public-Policy-Analytics-Landing/master/functions.r")
Data Wrangling
Base Data
Loading and Visualizing Chicago Data
Base data are about the geographic and crime data in Chicago. Point
plot and density plot are used fot the visualization of police
districts, police beats, Chicago city boundary, and robbery data as
follows.
policeDistricts <-
st_read("https://data.cityofchicago.org/api/geospatial/fthy-xz3r?method=export&format=GeoJSON") %>%
st_transform('ESRI:102271') %>%
dplyr::select(District = dist_num)
policeBeats <-
st_read("https://data.cityofchicago.org/api/geospatial/aerh-rz74?method=export&format=GeoJSON") %>%
st_transform('ESRI:102271') %>%
dplyr::select(District = beat_num)
bothPoliceUnits <- rbind(mutate(policeDistricts, Legend = "Police Districts"),
mutate(policeBeats, Legend = "Police Beats"))
robbery <-
read.socrata("https://data.cityofchicago.org/Public-Safety/Crimes-2020/qzdf-xmn8") %>%
filter(Primary.Type == "ROBBERY" & Description == "ARMED - HANDGUN") %>%
mutate(x = gsub("[()]", "", Location)) %>%
separate(x,into= c("Y","X"), sep=",") %>%
mutate(X = as.numeric(X),Y = as.numeric(Y)) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant")%>%
st_transform('ESRI:102271') %>%
distinct()
chicagoBoundary <-
st_read(file.path(root.dir,"/Chapter5/chicagoBoundary.geojson")) %>%
st_transform('ESRI:102271')
grid.arrange(ncol=2,
ggplot() +
geom_sf(data = chicagoBoundary) +
geom_sf(data = robbery, colour="#c44536", size=0.1, show.legend = "point") +
labs(title= "Robbery",
subtitle = "Chicago - 2020") +
mapTheme(title_size = 14),
ggplot() +
geom_sf(data = chicagoBoundary, fill = "grey80") +
stat_density2d(data = data.frame(st_coordinates(robbery)),
aes(X, Y, fill = ..level.., alpha = ..level..),
size = 0.01, bins = 40, geom = 'polygon') +
scale_fill_viridis() +
scale_alpha(range = c(0.00, 0.35), guide = FALSE) +
labs(title = "Density of Robbery",
subtitle = "Chicago - 2020") +
mapTheme(title_size = 14) + theme(legend.position = "none"))

Robbery Fishnet Plot
Robbery fishnet plot has been created according to the robbery data.
It can be seen clearly that most robberies happen in the northwest and
south of the city.
fishnet <-
st_make_grid(chicagoBoundary,
cellsize = 500,
square = TRUE) %>%
.[chicagoBoundary] %>%
st_sf() %>%
mutate(uniqueID = 1:n())
crime_net <-
dplyr::select(robbery) %>%
mutate(countRobbery = 1) %>%
aggregate(., fishnet, sum) %>%
mutate(countRobbery = replace_na(countRobbery, 0),
uniqueID = 1:n(),
cvID = sample(round(nrow(fishnet) / 24),
size=nrow(fishnet), replace = TRUE))
ggplot() +
geom_sf(data = crime_net, aes(fill = countRobbery), color = NA) +
scale_fill_viridis() +
labs(title = "Count of Robbery for the Fishnet",
subtitle = "Chicago - 2020") +
mapTheme()

Additional Spatial Features
Adding Other Spatial Features
Spatial features such as abandoned cars, abandoned buildings, shot
spotter alerts, street lights condition, and traffic crash are selected
to explore the relationship of these city features and crime rate.
abandonCars <-
read.socrata("https://data.cityofchicago.org/Service-Requests/311-Service-Requests-Abandoned-Vehicles/3c9v-pnva") %>%
mutate(year = substr(creation_date,1,4)) %>% filter(year == "2017") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Abandoned_Cars")
abandonBuildings <-
read.socrata("https://data.cityofchicago.org/Service-Requests/311-Service-Requests-Vacant-and-Abandoned-Building/7nii-7srd") %>%
mutate(year = substr(date_service_request_was_received,1,4)) %>% filter(year == "2017") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Abandoned_Buildings")
shotspotterAlerts <-
read.socrata("https://data.cityofchicago.org/Public-Safety/Violence-Reduction-Shotspotter-Alerts/3h7q-7mdb") %>%
mutate(year = substr(date,1,4)) %>% filter(year == "2017") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Shotspotter_Alerts")
streetlightsOut <-
read.socrata("https://data.cityofchicago.org/Service-Requests/311-Service-Requests-Street-Lights-All-Out/zuxi-7xem") %>%
mutate(year = substr(creation_date,1,4)) %>% filter(year == "2017") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Street_Lights_Out")
TrafficCrash <-
read.socrata("https://data.cityofchicago.org/Transportation/Traffic-Crashes-Crashes/85ca-t3if") %>%
mutate(year = substr(crash_date,1,4)) %>% filter(year == "2017") %>%
dplyr::select(Y = latitude, X = longitude) %>%
na.omit() %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform(st_crs(fishnet)) %>%
mutate(Legend = "Traffic_Crash")
neighborhoods <-
st_read("https://raw.githubusercontent.com/blackmad/neighborhoods/master/chicago.geojson") %>%
st_transform(st_crs(fishnet))
Fishnet Risk Features
Fishnet plots are also used to show the distribution and density of
the spatial features. Most abandoned buildings concentrate in the
southern part of Chicago, and others are in the northwest. The shot
spotter alert has similar distribution, but the coverage is much
smaller. Most abandoned cars are in north and west, and there’s no clear
trend of street lights out shown in the plot. The distributions of both
abandoned cars and street lights out are more scattered compared to the
former two features.
vars_net <-
rbind(abandonCars, abandonBuildings, streetlightsOut, shotspotterAlerts, TrafficCrash) %>%
st_join(fishnet, join=st_within) %>%
st_drop_geometry() %>%
group_by(uniqueID, Legend) %>%
summarize(count = n()) %>%
left_join(fishnet, ., by = "uniqueID") %>%
spread(Legend, count, fill=0) %>%
dplyr::select(-`<NA>`) %>%
na.omit() %>%
ungroup()
vars_net.long <-
gather(vars_net, Variable, value, -geometry, -uniqueID)
vars <- unique(vars_net.long$Variable)
mapList <- list()
for(i in vars){
mapList[[i]] <-
ggplot() +
geom_sf(data = filter(vars_net.long, Variable == i), aes(fill=value), colour=NA) +
scale_fill_viridis(name="") +
labs(title=i) +
mapTheme()}
do.call(grid.arrange,c(mapList, ncol=3,nrow=2, top="Risk Factors by Fishnet"))

Nearest Neighbor Features
By doing the nearest neighbor calculation, the distance of the
nearest three features of each type can be visualized as follows. These
results serve as important independent variables in the regression
model.
st_c <- st_coordinates
st_coid <- st_centroid
vars_net <- vars_net %>%
mutate(Abandoned_Cars.nn = nn_function(st_c(st_coid(vars_net)),
st_c(abandonCars),
k = 3),
Abandoned_Buildings.nn = nn_function(st_c(st_coid(vars_net)),
st_c(abandonBuildings),
k = 3),
Street_Lights_Out.nn = nn_function(st_c(st_coid(vars_net)),
st_c(streetlightsOut),
k = 3),
Shot_Spotter_Alerts.nn = nn_function(st_c(st_coid(vars_net)),
st_c(shotspotterAlerts),
k = 3),
Traffic_Crash.nn = nn_function(st_c(st_coid(vars_net)),
na.omit(st_c(TrafficCrash)),
k = 3)
)
vars_net.long.nn <-
dplyr::select(vars_net, ends_with(".nn")) %>%
gather(Variable, value, -geometry)
vars <- unique(vars_net.long.nn$Variable)
mapList <- list()
for(i in vars){
mapList[[i]] <-
ggplot() +
geom_sf(data = filter(vars_net.long.nn, Variable == i), aes(fill=value), colour=NA) +
scale_fill_viridis(name="") +
labs(title=i) +
mapTheme()}
do.call(grid.arrange,c(mapList, ncol = 3, top = "Nearest Neighbor risk Features by Fishnet"))

Join NN feature to our fishnet & Join in areal data
The following map shows the distribution of districts in Chicago.
final_net <-
left_join(crime_net, st_drop_geometry(vars_net), by="uniqueID")
final_net <-
st_centroid(final_net) %>%
st_join(dplyr::select(neighborhoods, name), by = "uniqueID") %>%
st_join(dplyr::select(policeDistricts, District), by = "uniqueID") %>%
st_drop_geometry() %>%
left_join(dplyr::select(final_net, geometry, uniqueID)) %>%
st_sf() %>%
na.omit()
mapview::mapview(final_net, zcol = "District")
Spatial Correlation
Local Moran’s I
Local Moran’s I is calculated and the hotspots of robbery are
identified according to the Moran’s I result. It can be seen from the
plot below that there are some small significant clusters of robberies
in Northwest and South Chicago. This again point out the importance to
account for spatial features when predicting the robbery pattern.
final_net.nb <- poly2nb(as_Spatial(final_net), queen=TRUE)
final_net.weights <- nb2listw(final_net.nb, style="W", zero.policy=TRUE)
local_morans <- localmoran(final_net$countRobbery, final_net.weights, zero.policy=TRUE) %>%
as.data.frame()
final_net.localMorans <-
cbind(local_morans, as.data.frame(final_net)) %>%
st_sf() %>%
dplyr::select(Robbery_count = countRobbery,
Local_Morans_I = Ii,
P_Value = `Pr(z != E(Ii))`) %>%
mutate(Significant_Hotspots = ifelse(P_Value <= 0.001, 1, 0)) %>%
gather(Variable, Value, -geometry)
vars <- unique(final_net.localMorans$Variable)
mapList <- list()
for(i in vars){
mapList[[i]] <-
ggplot() +
geom_sf(data = filter(final_net.localMorans, Variable == i),
aes(fill = Value), colour=NA) +
scale_fill_viridis(name="") +
labs(title=i) +
mapTheme(title_size = 14) + theme(legend.position="bottom")}
do.call(grid.arrange,c(mapList, ncol = 2, top = "Local Morans I statistics, Robbery"))

Correlations Scatterplots and Histogram of Robbery Counts
The correlation test results are shown as follows. Some predictors,
like abandoned buildings, shot spotter alerts, and traffic crash show
moderate correlations, while the relationship of other features, like
abandoned cars, with dependent variable seem to be weak. Meanwhile, the
correlation of spatial features are positive, while the relationship
between robbery and nearest neighbor features are negative.
The following histogram shows the distribution of robberies, which is
like Poisson distribution.
correlation_long <-
st_drop_geometry(final_net)%>%
dplyr::select(-uniqueID, -cvID, -name, -District) %>%
pivot_longer(cols = -countRobbery, # everything except measurement
names_to = "Type", # categorizes all quantitative variables into Type
values_to = "Number") # the name of values is Number
correlation_long %>%
ggplot(aes(x= Number, y = countRobbery)) +
geom_point(size = 0.1, color = "#283d3b") +
geom_smooth(method='lm', formula= y~x, lwd=0.5, color = "#c44536") +
facet_wrap(~ Type, scales = "free", labeller= labeller(Type = c(
`Abandoned_Buildings` = "Abandoned Buildings",
`Abandoned_Cars` = "Abandoned Cars",
`Shotspotter_Alerts` = "Shotspotter Alerts",
`Street_Lights_Out` = "Streetlights Out",
`Traffic_Crash` = "Traffic Crash",
`Abandoned_Buildings.nn` = "Abandoned Buildings.nn",
`Abandoned_Cars.nn` = "Abandoned Cars.nn",
`Shot_Spotter_Alerts.nn` = "Shotspotter Alerts.nn",
`Street_Lights_Out.nn` = "Streetlights Out.nn",
`Traffic_Crash.nn` = "Traffic Crash.nn"
))) +
labs(title = "Scatter Plot of Robbery over Risk Features") +
plotTheme()

ggplot(correlation_long, aes(x = countRobbery)) +
geom_histogram(binwidth = 1, fill = "#c44536", color = "#283d3b") +
labs(
title = "Histogram of Robbery Counts",
x = "Robbery Counts",
y = "Frequency"
) +
theme_minimal()

Distance to Hot spot
Below is the distance to hotspot plot.
final_net <- final_net %>%
mutate(robbery.isSig =
ifelse(local_morans[,5] <= 0.001, 1, 0)) %>%
mutate(robbery.isSig.dist =
nn_function(st_c(st_coid(final_net)),
st_c(st_coid(filter(final_net,
robbery.isSig == 1))),
k = 1))
ggplot() +
geom_sf(data = final_net, aes(fill=robbery.isSig.dist), colour=NA) +
scale_fill_viridis(name="NN Distance") +
labs(title="Robbery NN Distance") +
mapTheme()

Modeling
Fold and Spatial Regression
Just risk factors list contains only spatial variables, such as
abandoned cars, abandoned buildings, shot spotter alerts, and street
lights out. Based on this, spatial process list contains additional
crimehotspots like robbery.isSig and
robbery.isSig.dist. Flod and spatial regressions are done
using these variable lists.
reg.vars <- c("Abandoned_Buildings.nn", "Abandoned_Cars.nn", "Street_Lights_Out.nn", "Traffic_Crash.nn", "Shot_Spotter_Alerts.nn")
reg.ss.vars <- c("Abandoned_Buildings.nn", "Abandoned_Cars.nn", "Street_Lights_Out.nn", "Traffic_Crash.nn", "Shot_Spotter_Alerts.nn", "robbery.isSig", "robbery.isSig.dist")
reg.cv <- crossValidate(
dataset = final_net,
id = "cvID",
dependentVariable = "countRobbery",
indVariables = reg.vars) %>%
dplyr::select(cvID = cvID, countRobbery, Prediction, geometry)
reg.ss.cv <- crossValidate(
dataset = final_net,
id = "cvID",
dependentVariable = "countRobbery",
indVariables = reg.ss.vars) %>%
dplyr::select(cvID = cvID, countRobbery, Prediction, geometry)
reg.spatialCV <- crossValidate(
dataset = final_net,
id = "name",
dependentVariable = "countRobbery",
indVariables = reg.vars) %>%
dplyr::select(cvID = name, countRobbery, Prediction, geometry)
reg.ss.spatialCV <- crossValidate(
dataset = final_net,
id = "name",
dependentVariable = "countRobbery",
indVariables = reg.ss.vars) %>%
dplyr::select(cvID = name, countRobbery, Prediction, geometry)
reg.summary <-
rbind(
mutate(reg.cv, Error = Prediction - countRobbery,
Regression = "Random k-fold CV: Just Risk Factors"),
mutate(reg.ss.cv, Error = Prediction - countRobbery,
Regression = "Random k-fold CV: Spatial Process"),
mutate(reg.spatialCV, Error = Prediction - countRobbery,
Regression = "Spatial LOGO-CV: Just Risk Factors"),
mutate(reg.ss.spatialCV, Error = Prediction - countRobbery,
Regression = "Spatial LOGO-CV: Spatial Process")
) %>%
st_sf()
Calculating Errors across space
The accuracy of the regression model is measured. Mean error, MAE,
and SD MAE are shown in the maps, plots, and table below. It can be
concluded that random k-fold model functions better than spatial LOGO
model, and it seems that in the random k-fold model, spatial process
model works more accurate than just risk factors model.
error_by_reg_and_fold <-
reg.summary %>%
group_by(cvID, Regression) %>%
summarize(Mean_Error = mean(Prediction - countRobbery, na.rm = T),
MAE = mean(abs(Mean_Error), na.rm = T),
SD_MAE = mean(abs(Mean_Error), na.rm = T)) %>%
ungroup()
vars <- unique(error_by_reg_and_fold$Regression)
mapList <- list()
for(i in vars){
mapList[[i]] <-
ggplot() +
geom_sf(data = filter(error_by_reg_and_fold, Regression == i), aes(fill=MAE), colour=NA) +
scale_fill_viridis(name="") +
labs(title=i, size = 10) +
mapTheme()}
do.call(grid.arrange,c(mapList, ncol = 2, top = "MAE by Fold and Regression"))

error_by_reg_and_fold %>%
ggplot(aes(MAE)) +
geom_histogram(bins = 30, colour="#283d3b", fill = "#c44536") +
facet_wrap(~Regression) +
geom_vline(xintercept = 0) + scale_x_continuous(breaks = seq(0, 8, by = 1)) +
labs(title="Distribution of MAE", subtitle = "k-fold cross validation vs. LOGO-CV",
x="Mean Absolute Error", y="Count") +
plotTheme()

error_by_reg_and_fold <-
reg.summary %>%
group_by(Regression) %>%
summarize(Mean_Error = mean(Prediction - countRobbery, na.rm = T),
MAE = mean(abs(Mean_Error), na.rm = T),
SD_MAE = mean(abs(Mean_Error), na.rm = T)) %>%
ungroup()
error_by_reg_and_fold %>%
st_drop_geometry() %>%
kable() %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed")) %>%
row_spec(2, color = "#283d3b", background = "#e9ecef") %>%
row_spec(4, color = "#283d3b", background = "#e9ecef")
|
Regression
|
Mean_Error
|
MAE
|
SD_MAE
|
|
Random k-fold CV: Just Risk Factors
|
0.0001265
|
0.0001265
|
0.0001265
|
|
Random k-fold CV: Spatial Process
|
0.0002940
|
0.0002940
|
0.0002940
|
|
Spatial LOGO-CV: Just Risk Factors
|
-0.0054445
|
0.0054445
|
0.0054445
|
|
Spatial LOGO-CV: Spatial Process
|
-0.0064944
|
0.0064944
|
0.0064944
|
Errors by Race
The raw errors of model in race type are shown in the following
table. Random k-fold model still performs better than spatial LOGO model
in both majority white and majority non-white regions. However, the
error of spatial process model is very close to just risk factors model
in the comparison.
All the models show difference between majority white and majority
non-whit in error. Compared to model with just risk factors, the errors
of two regions are relevantly close to each other in model with spatial
process, which may indicate a better generalizability.
reg.summary %>%
st_centroid() %>%
st_join(tracts17) %>%
na.omit() %>%
st_drop_geometry() %>%
group_by(Regression, Race) %>%
summarize(mean.Error = mean(Error, na.rm = T)) %>%
spread(Race, mean.Error) %>%
kable(caption = "Mean Error by neighborhood racial context") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed")) %>%
row_spec(2, color = "#283d3b", background = "#e9ecef") %>%
row_spec(4, color = "#283d3b", background = "#e9ecef")
Mean Error by neighborhood racial context
|
Regression
|
MajorityNonWhite
|
MajorityWhite
|
|
Random k-fold CV: Just Risk Factors
|
-0.1118125
|
0.1196218
|
|
Random k-fold CV: Spatial Process
|
-0.0839205
|
0.0899487
|
|
Spatial LOGO-CV: Just Risk Factors
|
-0.1259531
|
0.1231963
|
|
Spatial LOGO-CV: Spatial Process
|
-0.1001091
|
0.0932068
|
Density vs predictions
The following plot is the kernel density of robberies in 2017. The
regions with high kernel density have a higher probability of robberies
in the next year.
rob_ppp <- as.ppp(st_coordinates(robbery), W = st_bbox(final_net))
rob_KD.1000 <- spatstat.explore::density.ppp(rob_ppp, 1000)
rob_KD.df <- rbind(
mutate(data.frame(rasterToPoints(mask(raster(rob_KD.1000), as(neighborhoods, 'Spatial')))), Legend = "1000 Ft."))
rob_KD.df$Legend <- factor(rob_KD.df$Legend, levels = c("1000 Ft."))
as.data.frame(rob_KD.1000) %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(final_net)) %>%
aggregate(., final_net, mean) %>%
ggplot() +
geom_sf(aes(fill=value)) +
geom_sf(data = sample_n(robbery, 1500), size = .5, color = "#283d3b") +
scale_fill_viridis(name = "Density") +
labs(title = "Kernel Density of 2020 Robbery") +
mapTheme(title_size = 14)
Compared to prediction made by kernel density analysis, the regression
model makes a more accurate prediction on the geographic level.
Regression model works more precisely in most parts of the city,
especially the southern part, but seems to underestimate the robbery
rank in north Chicago at the same time. Meanwhile, the result of kernel
density analysis is more reliable in northeast area, where regression
model fails to rank a higher level of robbery possibility.
robbery21 <-
read.socrata("https://data.cityofchicago.org/Public-Safety/Crimes-2021/dwme-t96c") %>%
filter(Primary.Type == "ROBBERY" & Description == "ARMED - HANDGUN") %>%
mutate(x = gsub("[()]", "", Location)) %>%
separate(x,into= c("Y","X"), sep=",") %>%
mutate(X = as.numeric(X),
Y = as.numeric(Y)) %>%
na.omit %>%
st_as_sf(coords = c("X", "Y"), crs = 4326, agr = "constant") %>%
st_transform('ESRI:102271') %>%
distinct() %>%
.[fishnet,]
rob_KDE_sum <- as.data.frame(rob_KD.1000) %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(final_net)) %>%
aggregate(., final_net, mean)
kde_breaks <- classIntervals(rob_KDE_sum$value,
n = 5, "fisher")
rob_KDE_sf <- rob_KDE_sum %>%
mutate(label = "Kernel Density",
Risk_Category = classInt::findCols(kde_breaks),
Risk_Category = case_when(
Risk_Category == 5 ~ "5th",
Risk_Category == 4 ~ "4th",
Risk_Category == 3 ~ "3rd",
Risk_Category == 2 ~ "2nd",
Risk_Category == 1 ~ "1st")) %>%
cbind(
aggregate(
dplyr::select(robbery21) %>% mutate(robberyCount = 1), ., sum) %>%
mutate(robberyCount = replace_na(robberyCount, 0))) %>%
dplyr::select(label, Risk_Category, robberyCount)
ml_breaks <- classIntervals(reg.ss.spatialCV$Prediction,
n = 5, "fisher")
rob_risk_sf <-
reg.ss.spatialCV %>%
mutate(label = "Risk Predictions",
Risk_Category =classInt::findCols(ml_breaks),
Risk_Category = case_when(
Risk_Category == 5 ~ "5th",
Risk_Category == 4 ~ "4th",
Risk_Category == 3 ~ "3rd",
Risk_Category == 2 ~ "2nd",
Risk_Category == 1 ~ "1st")) %>%
cbind(
aggregate(
dplyr::select(robbery21) %>% mutate(robberyCount = 1), ., sum) %>%
mutate(robberyCount = replace_na(robberyCount, 0))) %>%
dplyr::select(label,Risk_Category, robberyCount)
rbind(rob_KDE_sf, rob_risk_sf) %>%
na.omit() %>%
gather(Variable, Value, -label, -Risk_Category, -geometry) %>%
ggplot() +
geom_sf(aes(fill = Risk_Category), colour = NA) +
geom_sf(data = sample_n(robbery21, 3000, replace = TRUE), size = .5, colour = "#283d3b") +
facet_wrap(~label, ) +
scale_fill_viridis(discrete = TRUE) +
labs(title="Comparison of Kernel Density and Risk Predictions",
subtitle="2020 robbery risk predictions; 2021 robbery") +
mapTheme(title_size = 14)
As can be seen in the chart below, there’s a significant difference
between the result produced by kernel density analysis and the
prediction made regression model. Kernel density analysis predicts more
cases in high rank groups (3rd, 4th and 5th), especially in the 4th
rank, while regression model predicts more cases in low rank groups (1st
and 2nd), especially in the 1st rank. In general, kernel density
analysis predicts a increase trend in robbery cases and coverage, as
regression model suggests there should be a declined trend in 2021 on
the contrary.
rbind(rob_KDE_sf, rob_risk_sf) %>%
st_drop_geometry() %>%
na.omit() %>%
gather(Variable, Value, -label, -Risk_Category) %>%
group_by(label, Risk_Category) %>%
summarize(countRobbery = sum(Value)) %>%
ungroup() %>%
group_by(label) %>%
mutate(Pcnt_of_test_set_crimes = countRobbery / sum(countRobbery)) %>%
ggplot(aes(Risk_Category,Pcnt_of_test_set_crimes)) +
geom_bar(aes(fill=label), position="dodge", stat="identity") +
scale_fill_viridis(discrete = TRUE, name = "Model") +
labs(title = "Risk prediction vs. Kernel density, 2021 robbery",
y = "% of Test Set Robbery (per model)",
x = "Risk Category") +
theme_bw() +
theme(axis.text.x = element_text(angle = 45, vjust = 0.5))

Conclusion
In this project, geospatial features are examined and included in the
risk regression model to reveal the relationship between spatial
features and the crime attempts, especially robbery, in Chicago.
Although the predictions made by the model may not be precise enough in
some particular areas, this projection can explain the current crime
pattern in Chicago to some extent.
However, I will probably not recommend the regression model for
production use. Firstly, taking the future prediction as an example, the
performance of the regression model is not ideal in northern part of
Chicago: compared to kernel density analysis, it notably underestimates
robberies in the northeast. Moreover, the MAE and the standard deviation
of MAE are too small, which may indicate that the regression model is
overfitting. This will result in the decrease in generalizability, and
thus producing more errors in prediction. Additionally, the selected
independent variables can only partly represent some factors driving
robberies in Chicago. Other economic and demographic factors are
dismissed in this model, leading to an incomprehensive interpretation of
robberies. Furthermore, the base dataset is only about 2017, which is
not representative enough, and more data about other years should be
taken into consideration to produce a more reliable model.
Due to these reasons, I would less likely to recommend this
regression model for production use, but I will suggest that the model
can be considered as a reference in fitting suitable model for police
practice, since some variables in the regression model show significant
relationships with robberies and the model makes a well prediction in
some parts of Chicago in prediction.
LS0tCnRpdGxlOiAiUHJlZGljdGl2ZSBQb2xpY2luZyIKc3VidGl0bGU6ICJSb2JiZXJ5IFByZWRpY3Rpb24gTW9kZWwgaW4gQ2hpY2FnbyIKYXV0aG9yOiAiRSBDaGluIExpIgpkYXRlOiAiYDEwLzE1LzIwMjNgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBzaW1wbGV4CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCi0tLQojIEludHJvZHVjdGlvbiAmIFNldCB1cAoKQ2hpY2FnbydzIGNyaW1lIHJhdGVzIGhhdmUgbG9uZyBiZWVuIGEgc3ViamVjdCBvZiBuYXRpb25hbCBhbmQgaW50ZXJuYXRpb25hbCBjb25jZXJuIGFuZCB0aGUgY2l0eSBpdHNlbGYgc2VydmVzIGFzIGEgbWljcm9jb3NtIG9mIHVyYmFuIGNyaW1lIGNoYWxsZW5nZXMgZmFjZWQgYnkgbWFqb3IgbWV0cm9wb2xpdGFuIGFyZWFzIHdvcmxkd2lkZS4gQnkgZXhhbWluaW5nIHRoZSBwYXR0ZXJucyBhbmQgZGV0ZXJtaW5hbnRzIG9mIHJvYmJlcnkgaW4gQ2hpY2FnbywgdGhlIGR5bmFtaWNzIG9mIGNyaW1pbmFsIGFjdGl2aXRpZXMgY2FuIGJlIHVuZGVyc3Rvb2QgYmV0dGVyLCB3aGljaCBwcm92aWRlcyB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIGJyb2FkZXIgaXNzdWVzIHN1Y2ggYXMgc29jaWFsIGluZXF1YWxpdHksIHBvbGljaW5nIHN0cmF0ZWdpZXMsIGFuZCBjb21tdW5pdHkgc2FmZXR5LgoKU2VsZWN0aW9uIGJpYXMgaGFwcGVuIHdoZW4gcGxhY2VzIHRoYXQgaGF2ZSBzdWZmZXJlZCBmcm9tIHJvYmJlcnkgaW5jaWRlbnRzIGhpc3RvcmljYWxseSBhcmUgbGlrZWx5IHRvIGJlIHByZWRpY3RlZCB0byBoYXZlIGhpZ2hlciByb2JiZXJ5IHJhdGVzIHRoYW4gdXN1YWwuIFRoaXMgbWF5IGR1ZSB0byBjb21wbGljYXRlZCByZWFzb25zLiBVc2luZyBhIGRhdGFzZXQgZm9yIHNwZWNpZmljIHllYXIgZm9yIGFuYWx5c2lzIGNhbiBpbnRyb2R1Y2UgYmlhcywgYXMgcm9iYmVyeSBwYXR0ZXJucyBjYW4gdmFyeSBvdmVyIHRpbWUuIElmIHRoZSBjaG9zZW4gcGVyaW9kIGRvZXNuJ3QgY2FwdHVyZSB0aGVzZSB2YXJpYXRpb25zLCB0aGUgc3R1ZHkgcmVzdWx0cyBtYXkgbm90IGJlIGdlbmVyYWxpemFibGUuIE90aGVyIGZhY3RvcnMgbGlrZSB0aGUgc3Vydml2b3JzaGlwIGJpYXMgaW4gZGF0YSByZWNvcmRpbmcgbWF5IGFsc28gcmVzdWx0IGluIHNlbGVjdGlvbiBiaWFzIGluIG1vZGVsaW5nLiBUaGUgYmlhcyBjYW4gbGVhZCB0byBhbiB1bmRlcmVzdGltYXRpb24gb3Igb3ZlcmVzdGltYXRpb24gb2YgdGhlIHJpc2sgb2Ygcm9iYmVyeSBpbiBjZXJ0YWluIGFyZWFzLCB3aGljaCBjYW4gbGVhZCB0byBhbiBpbmNvbXBsZXRlIGFuZCBwb3RlbnRpYWxseSBza2V3ZWQgdW5kZXJzdGFuZGluZyBvZiB0aGUgZmFjdG9ycyBjb250cmlidXRpbmcgdG8gcm9iYmVyeSBhbmQgaXRzIHNwYXRpYWwgb3IgdGVtcG9yYWwgcGF0dGVybnMgaW4gQ2hpY2Fnby4gRnVydGhlcm1vcmUsIHB1YmxpYyBwb2xpY3kgYW5kIGxhdyBlbmZvcmNlbWVudCBzdHJhdGVnaWVzIG1heSBiZSBtaXNpbmZvcm1lZCBpZiBzZWxlY3Rpb24gYmlhcyBsZWFkcyB0byBpbmFjY3VyYXRlIGluc2lnaHRzIGludG8gaGlnaC1yaXNrIGFyZWFzIG9yIGRlbW9ncmFwaGljcy4KCiRBbGwgZGF0YSByZXNvdXJjZSBmcm9tIENoaWNhZ28gT3BlbiBEYXRhIHNpdGUgKGh0dHBzOi8vZGF0YS5jaXR5b2ZjaGljYWdvLm9yZy8pLiQKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKCWVjaG8gPSBUUlVFLAoJbWVzc2FnZSA9IEZBTFNFLAoJd2FybmluZyA9IEZBTFNFCikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmxpYnJhcnkoUlNvY3JhdGEpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShzcGF0c3RhdC5leHBsb3JlKQpsaWJyYXJ5KHJhc3RlcikKbGlicmFyeShzcGRlcCkKbGlicmFyeShGTk4pCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeSh0aWR5Y2Vuc3VzKQpsaWJyYXJ5KGNsYXNzSW50KSAgICMgZm9yIEtERSBhbmQgTUwgcmlzayBjbGFzcyBpbnRlcnZhbHMKIyBmdW5jdGlvbnMKcm9vdC5kaXIgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3VyYmFuU3BhdGlhbC9QdWJsaWMtUG9saWN5LUFuYWx5dGljcy1MYW5kaW5nL21hc3Rlci9EQVRBLyIKc291cmNlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vdXJiYW5TcGF0aWFsL1B1YmxpYy1Qb2xpY3ktQW5hbHl0aWNzLUxhbmRpbmcvbWFzdGVyL2Z1bmN0aW9ucy5yIikKCmBgYAoKIyBEYXRhIFdyYW5nbGluZwoKIyMgQmFzZSBEYXRhCgojIyMgTG9hZGluZyBhbmQgVmlzdWFsaXppbmcgQ2hpY2FnbyBEYXRhCgpCYXNlIGRhdGEgYXJlIGFib3V0IHRoZSBnZW9ncmFwaGljIGFuZCBjcmltZSBkYXRhIGluIENoaWNhZ28uIFBvaW50IHBsb3QgYW5kIGRlbnNpdHkgcGxvdCBhcmUgdXNlZCBmb3QgdGhlIHZpc3VhbGl6YXRpb24gb2YgcG9saWNlIGRpc3RyaWN0cywgcG9saWNlIGJlYXRzLCBDaGljYWdvIGNpdHkgYm91bmRhcnksIGFuZCByb2JiZXJ5IGRhdGEgYXMgZm9sbG93cy4KYGBge3IgZGF0YWxvYWRpbmcsIHJlc3VsdHMgPSAnaGlkZSd9CnBvbGljZURpc3RyaWN0cyA8LSAKICBzdF9yZWFkKCJodHRwczovL2RhdGEuY2l0eW9mY2hpY2Fnby5vcmcvYXBpL2dlb3NwYXRpYWwvZnRoeS14ejNyP21ldGhvZD1leHBvcnQmZm9ybWF0PUdlb0pTT04iKSAlPiUKICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyMjcxJykgJT4lCiAgZHBseXI6OnNlbGVjdChEaXN0cmljdCA9IGRpc3RfbnVtKQogIApwb2xpY2VCZWF0cyA8LSAKICBzdF9yZWFkKCJodHRwczovL2RhdGEuY2l0eW9mY2hpY2Fnby5vcmcvYXBpL2dlb3NwYXRpYWwvYWVyaC1yejc0P21ldGhvZD1leHBvcnQmZm9ybWF0PUdlb0pTT04iKSAlPiUKICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyMjcxJykgJT4lCiAgZHBseXI6OnNlbGVjdChEaXN0cmljdCA9IGJlYXRfbnVtKQoKYm90aFBvbGljZVVuaXRzIDwtIHJiaW5kKG11dGF0ZShwb2xpY2VEaXN0cmljdHMsIExlZ2VuZCA9ICJQb2xpY2UgRGlzdHJpY3RzIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKHBvbGljZUJlYXRzLCBMZWdlbmQgPSAiUG9saWNlIEJlYXRzIikpCgpyb2JiZXJ5IDwtIAogIHJlYWQuc29jcmF0YSgiaHR0cHM6Ly9kYXRhLmNpdHlvZmNoaWNhZ28ub3JnL1B1YmxpYy1TYWZldHkvQ3JpbWVzLTIwMjAvcXpkZi14bW44IikgJT4lIAogICAgZmlsdGVyKFByaW1hcnkuVHlwZSA9PSAiUk9CQkVSWSIgJiBEZXNjcmlwdGlvbiA9PSAiQVJNRUQgLSBIQU5ER1VOIikgJT4lCiAgICBtdXRhdGUoeCA9IGdzdWIoIlsoKV0iLCAiIiwgTG9jYXRpb24pKSAlPiUKICAgIHNlcGFyYXRlKHgsaW50bz0gYygiWSIsIlgiKSwgc2VwPSIsIikgJT4lCiAgICBtdXRhdGUoWCA9IGFzLm51bWVyaWMoWCksWSA9IGFzLm51bWVyaWMoWSkpICU+JSAKICAgIG5hLm9taXQoKSAlPiUKICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoIlgiLCAiWSIpLCBjcnMgPSA0MzI2LCBhZ3IgPSAiY29uc3RhbnQiKSU+JQogICAgc3RfdHJhbnNmb3JtKCdFU1JJOjEwMjI3MScpICU+JSAKICAgIGRpc3RpbmN0KCkKCmNoaWNhZ29Cb3VuZGFyeSA8LSAKICBzdF9yZWFkKGZpbGUucGF0aChyb290LmRpciwiL0NoYXB0ZXI1L2NoaWNhZ29Cb3VuZGFyeS5nZW9qc29uIikpICU+JQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDIyNzEnKSAKYGBgCgpgYGB7ciByb2JiZXJ5LCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD02LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKZ3JpZC5hcnJhbmdlKG5jb2w9MiwKZ2dwbG90KCkgKyAKICBnZW9tX3NmKGRhdGEgPSBjaGljYWdvQm91bmRhcnkpICsKICBnZW9tX3NmKGRhdGEgPSByb2JiZXJ5LCBjb2xvdXI9IiNjNDQ1MzYiLCBzaXplPTAuMSwgc2hvdy5sZWdlbmQgPSAicG9pbnQiKSArCiAgbGFicyh0aXRsZT0gIlJvYmJlcnkiLAogICAgICAgc3VidGl0bGUgPSAiQ2hpY2FnbyAtIDIwMjAiKSArCiAgbWFwVGhlbWUodGl0bGVfc2l6ZSA9IDE0KSwKCmdncGxvdCgpICsgCiAgZ2VvbV9zZihkYXRhID0gY2hpY2Fnb0JvdW5kYXJ5LCBmaWxsID0gImdyZXk4MCIpICsKICBzdGF0X2RlbnNpdHkyZChkYXRhID0gZGF0YS5mcmFtZShzdF9jb29yZGluYXRlcyhyb2JiZXJ5KSksIAogICAgICAgICAgICAgICAgIGFlcyhYLCBZLCBmaWxsID0gLi5sZXZlbC4uLCBhbHBoYSA9IC4ubGV2ZWwuLiksCiAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuMDEsIGJpbnMgPSA0MCwgZ2VvbSA9ICdwb2x5Z29uJykgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcygpICsKICBzY2FsZV9hbHBoYShyYW5nZSA9IGMoMC4wMCwgMC4zNSksIGd1aWRlID0gRkFMU0UpICsKICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgb2YgUm9iYmVyeSIsCiAgICAgICBzdWJ0aXRsZSA9ICJDaGljYWdvIC0gMjAyMCIpICsKICBtYXBUaGVtZSh0aXRsZV9zaXplID0gMTQpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkKYGBgCgojIyMgUm9iYmVyeSBGaXNobmV0IFBsb3QKClJvYmJlcnkgZmlzaG5ldCBwbG90IGhhcyBiZWVuIGNyZWF0ZWQgYWNjb3JkaW5nIHRvIHRoZSByb2JiZXJ5IGRhdGEuIEl0IGNhbiBiZSBzZWVuIGNsZWFybHkgdGhhdCBtb3N0IHJvYmJlcmllcyBoYXBwZW4gaW4gdGhlIG5vcnRod2VzdCBhbmQgc291dGggb2YgdGhlIGNpdHkuCmBgYHtyIHJvYmJlcnkgZmlzaG5ldCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZmlzaG5ldCA8LSAKICBzdF9tYWtlX2dyaWQoY2hpY2Fnb0JvdW5kYXJ5LAogICAgICAgICAgICAgICBjZWxsc2l6ZSA9IDUwMCwgCiAgICAgICAgICAgICAgIHNxdWFyZSA9IFRSVUUpICU+JQogIC5bY2hpY2Fnb0JvdW5kYXJ5XSAlPiUgICAgICAgICAgICAKICBzdF9zZigpICU+JQogIG11dGF0ZSh1bmlxdWVJRCA9IDE6bigpKQoKY3JpbWVfbmV0IDwtIAogIGRwbHlyOjpzZWxlY3Qocm9iYmVyeSkgJT4lIAogIG11dGF0ZShjb3VudFJvYmJlcnkgPSAxKSAlPiUgCiAgYWdncmVnYXRlKC4sIGZpc2huZXQsIHN1bSkgJT4lCiAgbXV0YXRlKGNvdW50Um9iYmVyeSA9IHJlcGxhY2VfbmEoY291bnRSb2JiZXJ5LCAwKSwKICAgICAgICAgdW5pcXVlSUQgPSAxOm4oKSwKICAgICAgICAgY3ZJRCA9IHNhbXBsZShyb3VuZChucm93KGZpc2huZXQpIC8gMjQpLCAKICAgICAgICAgICAgICAgICAgICAgICBzaXplPW5yb3coZmlzaG5ldCksIHJlcGxhY2UgPSBUUlVFKSkKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBjcmltZV9uZXQsIGFlcyhmaWxsID0gY291bnRSb2JiZXJ5KSwgY29sb3IgPSBOQSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcygpICsKICBsYWJzKHRpdGxlID0gIkNvdW50IG9mIFJvYmJlcnkgZm9yIHRoZSBGaXNobmV0IiwKICAgICAgIHN1YnRpdGxlID0gIkNoaWNhZ28gLSAyMDIwIikgKwogIG1hcFRoZW1lKCkKCmBgYAoKIyMgQWRkaXRpb25hbCBTcGF0aWFsIEZlYXR1cmVzCgojIyMgQWRkaW5nIE90aGVyIFNwYXRpYWwgRmVhdHVyZXMKClNwYXRpYWwgZmVhdHVyZXMgc3VjaCBhcyBhYmFuZG9uZWQgY2FycywgYWJhbmRvbmVkIGJ1aWxkaW5ncywgc2hvdCBzcG90dGVyIGFsZXJ0cywgc3RyZWV0IGxpZ2h0cyBjb25kaXRpb24sIGFuZCB0cmFmZmljIGNyYXNoIGFyZSBzZWxlY3RlZCB0byBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgb2YgdGhlc2UgY2l0eSBmZWF0dXJlcyBhbmQgY3JpbWUgcmF0ZS4KYGBge3IgZmVhdHVyZXMsIHJlc3VsdHMgPSAnaGlkZSd9CgphYmFuZG9uQ2FycyA8LSAKICByZWFkLnNvY3JhdGEoImh0dHBzOi8vZGF0YS5jaXR5b2ZjaGljYWdvLm9yZy9TZXJ2aWNlLVJlcXVlc3RzLzMxMS1TZXJ2aWNlLVJlcXVlc3RzLUFiYW5kb25lZC1WZWhpY2xlcy8zYzl2LXBudmEiKSAlPiUKICAgIG11dGF0ZSh5ZWFyID0gc3Vic3RyKGNyZWF0aW9uX2RhdGUsMSw0KSkgJT4lIGZpbHRlcih5ZWFyID09ICIyMDE3IikgJT4lCiAgICBkcGx5cjo6c2VsZWN0KFkgPSBsYXRpdHVkZSwgWCA9IGxvbmdpdHVkZSkgJT4lCiAgICBuYS5vbWl0KCkgJT4lCiAgICBzdF9hc19zZihjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gNDMyNiwgYWdyID0gImNvbnN0YW50IikgJT4lCiAgICBzdF90cmFuc2Zvcm0oc3RfY3JzKGZpc2huZXQpKSAlPiUKICAgIG11dGF0ZShMZWdlbmQgPSAiQWJhbmRvbmVkX0NhcnMiKQoKYWJhbmRvbkJ1aWxkaW5ncyA8LSAKICByZWFkLnNvY3JhdGEoImh0dHBzOi8vZGF0YS5jaXR5b2ZjaGljYWdvLm9yZy9TZXJ2aWNlLVJlcXVlc3RzLzMxMS1TZXJ2aWNlLVJlcXVlc3RzLVZhY2FudC1hbmQtQWJhbmRvbmVkLUJ1aWxkaW5nLzduaWktN3NyZCIpICU+JQogICAgbXV0YXRlKHllYXIgPSBzdWJzdHIoZGF0ZV9zZXJ2aWNlX3JlcXVlc3Rfd2FzX3JlY2VpdmVkLDEsNCkpICU+JSAgZmlsdGVyKHllYXIgPT0gIjIwMTciKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoWSA9IGxhdGl0dWRlLCBYID0gbG9uZ2l0dWRlKSAlPiUKICAgIG5hLm9taXQoKSAlPiUKICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoIlgiLCAiWSIpLCBjcnMgPSA0MzI2LCBhZ3IgPSAiY29uc3RhbnQiKSAlPiUKICAgIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpICU+JQogICAgbXV0YXRlKExlZ2VuZCA9ICJBYmFuZG9uZWRfQnVpbGRpbmdzIikKCnNob3RzcG90dGVyQWxlcnRzIDwtIAogIHJlYWQuc29jcmF0YSgiaHR0cHM6Ly9kYXRhLmNpdHlvZmNoaWNhZ28ub3JnL1B1YmxpYy1TYWZldHkvVmlvbGVuY2UtUmVkdWN0aW9uLVNob3RzcG90dGVyLUFsZXJ0cy8zaDdxLTdtZGIiKSAlPiUKICAgIG11dGF0ZSh5ZWFyID0gc3Vic3RyKGRhdGUsMSw0KSkgJT4lIGZpbHRlcih5ZWFyID09ICIyMDE3IikgJT4lCiAgICBkcGx5cjo6c2VsZWN0KFkgPSBsYXRpdHVkZSwgWCA9IGxvbmdpdHVkZSkgJT4lCiAgICBuYS5vbWl0KCkgJT4lCiAgICBzdF9hc19zZihjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gNDMyNiwgYWdyID0gImNvbnN0YW50IikgJT4lCiAgICBzdF90cmFuc2Zvcm0oc3RfY3JzKGZpc2huZXQpKSAlPiUKICAgIG11dGF0ZShMZWdlbmQgPSAiU2hvdHNwb3R0ZXJfQWxlcnRzIikKCnN0cmVldGxpZ2h0c091dCA8LSAKICByZWFkLnNvY3JhdGEoImh0dHBzOi8vZGF0YS5jaXR5b2ZjaGljYWdvLm9yZy9TZXJ2aWNlLVJlcXVlc3RzLzMxMS1TZXJ2aWNlLVJlcXVlc3RzLVN0cmVldC1MaWdodHMtQWxsLU91dC96dXhpLTd4ZW0iKSAlPiUKICBtdXRhdGUoeWVhciA9IHN1YnN0cihjcmVhdGlvbl9kYXRlLDEsNCkpICU+JSBmaWx0ZXIoeWVhciA9PSAiMjAxNyIpICU+JQogIGRwbHlyOjpzZWxlY3QoWSA9IGxhdGl0dWRlLCBYID0gbG9uZ2l0dWRlKSAlPiUKICBuYS5vbWl0KCkgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygiWCIsICJZIiksIGNycyA9IDQzMjYsIGFnciA9ICJjb25zdGFudCIpICU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpICU+JQogIG11dGF0ZShMZWdlbmQgPSAiU3RyZWV0X0xpZ2h0c19PdXQiKQoKVHJhZmZpY0NyYXNoIDwtIAogIHJlYWQuc29jcmF0YSgiaHR0cHM6Ly9kYXRhLmNpdHlvZmNoaWNhZ28ub3JnL1RyYW5zcG9ydGF0aW9uL1RyYWZmaWMtQ3Jhc2hlcy1DcmFzaGVzLzg1Y2EtdDNpZiIpICU+JQogIG11dGF0ZSh5ZWFyID0gc3Vic3RyKGNyYXNoX2RhdGUsMSw0KSkgJT4lIGZpbHRlcih5ZWFyID09ICIyMDE3IikgJT4lCiAgZHBseXI6OnNlbGVjdChZID0gbGF0aXR1ZGUsIFggPSBsb25naXR1ZGUpICU+JQogIG5hLm9taXQoKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gNDMyNiwgYWdyID0gImNvbnN0YW50IikgJT4lCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhmaXNobmV0KSkgJT4lCiAgbXV0YXRlKExlZ2VuZCA9ICJUcmFmZmljX0NyYXNoIikKCm5laWdoYm9yaG9vZHMgPC0gCiAgc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2JsYWNrbWFkL25laWdoYm9yaG9vZHMvbWFzdGVyL2NoaWNhZ28uZ2VvanNvbiIpICU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZmlzaG5ldCkpIAoKYGBgCgojIyMgRmlzaG5ldCBSaXNrIEZlYXR1cmVzCgpGaXNobmV0IHBsb3RzIGFyZSBhbHNvIHVzZWQgdG8gc2hvdyB0aGUgZGlzdHJpYnV0aW9uIGFuZCBkZW5zaXR5IG9mIHRoZSBzcGF0aWFsIGZlYXR1cmVzLiBNb3N0IGFiYW5kb25lZCBidWlsZGluZ3MgY29uY2VudHJhdGUgaW4gdGhlIHNvdXRoZXJuIHBhcnQgb2YgQ2hpY2FnbywgYW5kIG90aGVycyBhcmUgaW4gdGhlIG5vcnRod2VzdC4gVGhlIHNob3Qgc3BvdHRlciBhbGVydCBoYXMgc2ltaWxhciBkaXN0cmlidXRpb24sIGJ1dCB0aGUgY292ZXJhZ2UgaXMgbXVjaCBzbWFsbGVyLiBNb3N0IGFiYW5kb25lZCBjYXJzIGFyZSBpbiBub3J0aCBhbmQgd2VzdCwgYW5kIHRoZXJlJ3Mgbm8gY2xlYXIgdHJlbmQgb2Ygc3RyZWV0IGxpZ2h0cyBvdXQgc2hvd24gaW4gdGhlIHBsb3QuIFRoZSBkaXN0cmlidXRpb25zIG9mIGJvdGggYWJhbmRvbmVkIGNhcnMgYW5kIHN0cmVldCBsaWdodHMgb3V0IGFyZSBtb3JlIHNjYXR0ZXJlZCBjb21wYXJlZCB0byB0aGUgZm9ybWVyIHR3byBmZWF0dXJlcy4KYGBge3IgZmlzaG5ldCByaXNrIGZlYXR1cmVzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKdmFyc19uZXQgPC0gCiAgcmJpbmQoYWJhbmRvbkNhcnMsIGFiYW5kb25CdWlsZGluZ3MsIHN0cmVldGxpZ2h0c091dCwgc2hvdHNwb3R0ZXJBbGVydHMsIFRyYWZmaWNDcmFzaCkgJT4lCiAgc3Rfam9pbihmaXNobmV0LCBqb2luPXN0X3dpdGhpbikgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogIGdyb3VwX2J5KHVuaXF1ZUlELCBMZWdlbmQpICU+JQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSkgJT4lCiAgbGVmdF9qb2luKGZpc2huZXQsIC4sIGJ5ID0gInVuaXF1ZUlEIikgJT4lCiAgc3ByZWFkKExlZ2VuZCwgY291bnQsIGZpbGw9MCkgJT4lCiAgZHBseXI6OnNlbGVjdCgtYDxOQT5gKSAlPiUKICBuYS5vbWl0KCkgJT4lCiAgdW5ncm91cCgpCgp2YXJzX25ldC5sb25nIDwtIAogIGdhdGhlcih2YXJzX25ldCwgVmFyaWFibGUsIHZhbHVlLCAtZ2VvbWV0cnksIC11bmlxdWVJRCkKCnZhcnMgPC0gdW5pcXVlKHZhcnNfbmV0LmxvbmckVmFyaWFibGUpCm1hcExpc3QgPC0gbGlzdCgpCgpmb3IoaSBpbiB2YXJzKXsKICBtYXBMaXN0W1tpXV0gPC0gCiAgICBnZ3Bsb3QoKSArCiAgICAgIGdlb21fc2YoZGF0YSA9IGZpbHRlcih2YXJzX25ldC5sb25nLCBWYXJpYWJsZSA9PSBpKSwgYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXI9TkEpICsKICAgICAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWU9IiIpICsKICAgICAgbGFicyh0aXRsZT1pKSArCiAgICAgIG1hcFRoZW1lKCl9Cgpkby5jYWxsKGdyaWQuYXJyYW5nZSxjKG1hcExpc3QsIG5jb2w9Myxucm93PTIsIHRvcD0iUmlzayBGYWN0b3JzIGJ5IEZpc2huZXQiKSkKCmBgYAoKIyMjIE5lYXJlc3QgTmVpZ2hib3IgRmVhdHVyZXMKCkJ5IGRvaW5nIHRoZSBuZWFyZXN0IG5laWdoYm9yIGNhbGN1bGF0aW9uLCB0aGUgZGlzdGFuY2Ugb2YgdGhlIG5lYXJlc3QgdGhyZWUgZmVhdHVyZXMgb2YgZWFjaCB0eXBlIGNhbiBiZSB2aXN1YWxpemVkIGFzIGZvbGxvd3MuIFRoZXNlIHJlc3VsdHMgc2VydmUgYXMgaW1wb3J0YW50IGluZGVwZW5kZW50IHZhcmlhYmxlcyBpbiB0aGUgcmVncmVzc2lvbiBtb2RlbC4KYGBge3Igbm4gZmVhdHVyZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpzdF9jICAgIDwtIHN0X2Nvb3JkaW5hdGVzCnN0X2NvaWQgPC0gc3RfY2VudHJvaWQKCnZhcnNfbmV0IDwtIHZhcnNfbmV0ICU+JQogICAgbXV0YXRlKEFiYW5kb25lZF9DYXJzLm5uID0gbm5fZnVuY3Rpb24oc3RfYyhzdF9jb2lkKHZhcnNfbmV0KSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RfYyhhYmFuZG9uQ2FycyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrID0gMyksCiAgICAgICAgICAgQWJhbmRvbmVkX0J1aWxkaW5ncy5ubiA9IG5uX2Z1bmN0aW9uKHN0X2Moc3RfY29pZCh2YXJzX25ldCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0X2MoYWJhbmRvbkJ1aWxkaW5ncyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrID0gMyksCiAgICAgICAgICAgU3RyZWV0X0xpZ2h0c19PdXQubm4gPSBubl9mdW5jdGlvbihzdF9jKHN0X2NvaWQodmFyc19uZXQpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdF9jKHN0cmVldGxpZ2h0c091dCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrID0gMyksCiAgICAgICAgICAgU2hvdF9TcG90dGVyX0FsZXJ0cy5ubiA9IG5uX2Z1bmN0aW9uKHN0X2Moc3RfY29pZCh2YXJzX25ldCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0X2Moc2hvdHNwb3R0ZXJBbGVydHMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgayA9IDMpLAogICAgICAgICAgIFRyYWZmaWNfQ3Jhc2gubm4gPSBubl9mdW5jdGlvbihzdF9jKHN0X2NvaWQodmFyc19uZXQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEub21pdChzdF9jKFRyYWZmaWNDcmFzaCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrID0gMykKICAgICAgICAgICApCgp2YXJzX25ldC5sb25nLm5uIDwtIAogIGRwbHlyOjpzZWxlY3QodmFyc19uZXQsIGVuZHNfd2l0aCgiLm5uIikpICU+JQogICAgZ2F0aGVyKFZhcmlhYmxlLCB2YWx1ZSwgLWdlb21ldHJ5KQoKdmFycyA8LSB1bmlxdWUodmFyc19uZXQubG9uZy5ubiRWYXJpYWJsZSkKbWFwTGlzdCA8LSBsaXN0KCkKCmZvcihpIGluIHZhcnMpewogIG1hcExpc3RbW2ldXSA8LSAKICAgIGdncGxvdCgpICsKICAgIGdlb21fc2YoZGF0YSA9IGZpbHRlcih2YXJzX25ldC5sb25nLm5uLCBWYXJpYWJsZSA9PSBpKSwgYWVzKGZpbGw9dmFsdWUpLCBjb2xvdXI9TkEpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lPSIiKSArCiAgICBsYWJzKHRpdGxlPWkpICsKICAgIG1hcFRoZW1lKCl9Cgpkby5jYWxsKGdyaWQuYXJyYW5nZSxjKG1hcExpc3QsIG5jb2wgPSAzLCB0b3AgPSAiTmVhcmVzdCBOZWlnaGJvciByaXNrIEZlYXR1cmVzIGJ5IEZpc2huZXQiKSkKYGBgCgojIyBKb2luIE5OIGZlYXR1cmUgdG8gb3VyIGZpc2huZXQgJiBKb2luIGluIGFyZWFsIGRhdGEKClRoZSBmb2xsb3dpbmcgbWFwIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgZGlzdHJpY3RzIGluIENoaWNhZ28uCmBgYHtyIGpvaW4gbm4gZmVhdHVyZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZmluYWxfbmV0IDwtCiAgbGVmdF9qb2luKGNyaW1lX25ldCwgc3RfZHJvcF9nZW9tZXRyeSh2YXJzX25ldCksIGJ5PSJ1bmlxdWVJRCIpIAoKZmluYWxfbmV0IDwtCiAgc3RfY2VudHJvaWQoZmluYWxfbmV0KSAlPiUKICAgIHN0X2pvaW4oZHBseXI6OnNlbGVjdChuZWlnaGJvcmhvb2RzLCBuYW1lKSwgYnkgPSAidW5pcXVlSUQiKSAlPiUKICAgIHN0X2pvaW4oZHBseXI6OnNlbGVjdChwb2xpY2VEaXN0cmljdHMsIERpc3RyaWN0KSwgYnkgPSAidW5pcXVlSUQiKSAlPiUKICAgICAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogICAgICBsZWZ0X2pvaW4oZHBseXI6OnNlbGVjdChmaW5hbF9uZXQsIGdlb21ldHJ5LCB1bmlxdWVJRCkpICU+JQogICAgICBzdF9zZigpICU+JQogIG5hLm9taXQoKQoKbWFwdmlldzo6bWFwdmlldyhmaW5hbF9uZXQsIHpjb2wgPSAiRGlzdHJpY3QiKQpgYGAKCiMgU3BhdGlhbCBDb3JyZWxhdGlvbgoKIyMgTG9jYWwgTW9yYW4ncyBJIAoKTG9jYWwgTW9yYW7igJlzIEkgaXMgY2FsY3VsYXRlZCBhbmQgdGhlIGhvdHNwb3RzIG9mIHJvYmJlcnkgYXJlIGlkZW50aWZpZWQgYWNjb3JkaW5nIHRvIHRoZSBNb3JhbuKAmXMgSSByZXN1bHQuIEl0IGNhbiBiZSBzZWVuIGZyb20gdGhlIHBsb3QgYmVsb3cgdGhhdCB0aGVyZSBhcmUgc29tZSBzbWFsbCBzaWduaWZpY2FudCBjbHVzdGVycyBvZiByb2JiZXJpZXMgaW4gTm9ydGh3ZXN0IGFuZCBTb3V0aCBDaGljYWdvLiBUaGlzIGFnYWluIHBvaW50IG91dCB0aGUgaW1wb3J0YW5jZSB0byBhY2NvdW50IGZvciBzcGF0aWFsIGZlYXR1cmVzIHdoZW4gcHJlZGljdGluZyB0aGUgcm9iYmVyeSBwYXR0ZXJuLgpgYGB7ciBtb3Jhbiwgd2FybmluZz1GQUxTRX0KZmluYWxfbmV0Lm5iIDwtIHBvbHkybmIoYXNfU3BhdGlhbChmaW5hbF9uZXQpLCBxdWVlbj1UUlVFKQpmaW5hbF9uZXQud2VpZ2h0cyA8LSBuYjJsaXN0dyhmaW5hbF9uZXQubmIsIHN0eWxlPSJXIiwgemVyby5wb2xpY3k9VFJVRSkKCmxvY2FsX21vcmFucyA8LSBsb2NhbG1vcmFuKGZpbmFsX25ldCRjb3VudFJvYmJlcnksIGZpbmFsX25ldC53ZWlnaHRzLCB6ZXJvLnBvbGljeT1UUlVFKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgpmaW5hbF9uZXQubG9jYWxNb3JhbnMgPC0gCiAgY2JpbmQobG9jYWxfbW9yYW5zLCBhcy5kYXRhLmZyYW1lKGZpbmFsX25ldCkpICU+JSAKICBzdF9zZigpICU+JQogIGRwbHlyOjpzZWxlY3QoUm9iYmVyeV9jb3VudCA9IGNvdW50Um9iYmVyeSwgCiAgICAgICAgICAgICAgICBMb2NhbF9Nb3JhbnNfSSA9IElpLCAKICAgICAgICAgICAgICAgIFBfVmFsdWUgPSBgUHIoeiAhPSBFKElpKSlgKSAlPiUKICBtdXRhdGUoU2lnbmlmaWNhbnRfSG90c3BvdHMgPSBpZmVsc2UoUF9WYWx1ZSA8PSAwLjAwMSwgMSwgMCkpICU+JQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1nZW9tZXRyeSkKCnZhcnMgPC0gdW5pcXVlKGZpbmFsX25ldC5sb2NhbE1vcmFucyRWYXJpYWJsZSkKbWFwTGlzdCA8LSBsaXN0KCkKCmZvcihpIGluIHZhcnMpewogIG1hcExpc3RbW2ldXSA8LSAKICAgIGdncGxvdCgpICsKICAgICAgZ2VvbV9zZihkYXRhID0gZmlsdGVyKGZpbmFsX25ldC5sb2NhbE1vcmFucywgVmFyaWFibGUgPT0gaSksIAogICAgICAgICAgICAgIGFlcyhmaWxsID0gVmFsdWUpLCBjb2xvdXI9TkEpICsKICAgICAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWU9IiIpICsKICAgICAgbGFicyh0aXRsZT1pKSArCiAgICAgIG1hcFRoZW1lKHRpdGxlX3NpemUgPSAxNCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpfQoKZG8uY2FsbChncmlkLmFycmFuZ2UsYyhtYXBMaXN0LCBuY29sID0gMiwgdG9wID0gIkxvY2FsIE1vcmFucyBJIHN0YXRpc3RpY3MsIFJvYmJlcnkiKSkKICAKYGBgCgojIyBDb3JyZWxhdGlvbnMgU2NhdHRlcnBsb3RzIGFuZCBIaXN0b2dyYW0gb2YgUm9iYmVyeSBDb3VudHMKClRoZSBjb3JyZWxhdGlvbiB0ZXN0IHJlc3VsdHMgYXJlIHNob3duIGFzIGZvbGxvd3MuIFNvbWUgcHJlZGljdG9ycywgbGlrZSBhYmFuZG9uZWQgYnVpbGRpbmdzLCBzaG90IHNwb3R0ZXIgYWxlcnRzLCBhbmQgdHJhZmZpYyBjcmFzaCBzaG93IG1vZGVyYXRlIGNvcnJlbGF0aW9ucywgd2hpbGUgdGhlIHJlbGF0aW9uc2hpcCBvZiBvdGhlciBmZWF0dXJlcywgbGlrZSBhYmFuZG9uZWQgY2Fycywgd2l0aCBkZXBlbmRlbnQgdmFyaWFibGUgc2VlbSB0byBiZSB3ZWFrLiBNZWFud2hpbGUsIHRoZSBjb3JyZWxhdGlvbiBvZiBzcGF0aWFsIGZlYXR1cmVzIGFyZSBwb3NpdGl2ZSwgd2hpbGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHJvYmJlcnkgYW5kIG5lYXJlc3QgbmVpZ2hib3IgZmVhdHVyZXMgYXJlIG5lZ2F0aXZlLgoKVGhlIGZvbGxvd2luZyBoaXN0b2dyYW0gc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiByb2JiZXJpZXMsIHdoaWNoIGlzIGxpa2UgUG9pc3NvbiBkaXN0cmlidXRpb24uCmBgYHtyIHNjYXR0ZXIgcGxvdCBhbmQgaGlzdG9ncmFtLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmNvcnJlbGF0aW9uX2xvbmcgPC0gCiAgc3RfZHJvcF9nZW9tZXRyeShmaW5hbF9uZXQpJT4lIAogIGRwbHlyOjpzZWxlY3QoLXVuaXF1ZUlELCAtY3ZJRCwgLW5hbWUsIC1EaXN0cmljdCkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtY291bnRSb2JiZXJ5LCAjIGV2ZXJ5dGhpbmcgZXhjZXB0IG1lYXN1cmVtZW50CiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlR5cGUiLCAjIGNhdGVnb3JpemVzIGFsbCBxdWFudGl0YXRpdmUgdmFyaWFibGVzIGludG8gVHlwZQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiTnVtYmVyIikgIyB0aGUgbmFtZSBvZiB2YWx1ZXMgaXMgTnVtYmVyCgogY29ycmVsYXRpb25fbG9uZyAlPiUKICBnZ3Bsb3QoYWVzKHg9IE51bWJlciwgeSA9IGNvdW50Um9iYmVyeSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjEsIGNvbG9yID0gIiMyODNkM2IiKSArICAKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgZm9ybXVsYT0geX54LCBsd2Q9MC41LCBjb2xvciA9ICIjYzQ0NTM2IikgKwogIGZhY2V0X3dyYXAofiBUeXBlLCBzY2FsZXMgPSAiZnJlZSIsIGxhYmVsbGVyPSBsYWJlbGxlcihUeXBlID0gYygKICAgIGBBYmFuZG9uZWRfQnVpbGRpbmdzYCA9ICJBYmFuZG9uZWQgQnVpbGRpbmdzIiwKICAgIGBBYmFuZG9uZWRfQ2Fyc2AgPSAiQWJhbmRvbmVkIENhcnMiLAogICAgYFNob3RzcG90dGVyX0FsZXJ0c2AgPSAiU2hvdHNwb3R0ZXIgQWxlcnRzIiwKICAgIGBTdHJlZXRfTGlnaHRzX091dGAgPSAiU3RyZWV0bGlnaHRzIE91dCIsCiAgICBgVHJhZmZpY19DcmFzaGAgPSAiVHJhZmZpYyBDcmFzaCIsCiAgICBgQWJhbmRvbmVkX0J1aWxkaW5ncy5ubmAgPSAiQWJhbmRvbmVkIEJ1aWxkaW5ncy5ubiIsCiAgICBgQWJhbmRvbmVkX0NhcnMubm5gID0gIkFiYW5kb25lZCBDYXJzLm5uIiwKICAgIGBTaG90X1Nwb3R0ZXJfQWxlcnRzLm5uYCA9ICJTaG90c3BvdHRlciBBbGVydHMubm4iLAogICAgYFN0cmVldF9MaWdodHNfT3V0Lm5uYCA9ICJTdHJlZXRsaWdodHMgT3V0Lm5uIiwKICAgIGBUcmFmZmljX0NyYXNoLm5uYCA9ICJUcmFmZmljIENyYXNoLm5uIgogICAgKSkpICArCiAgbGFicyh0aXRsZSA9ICJTY2F0dGVyIFBsb3Qgb2YgUm9iYmVyeSBvdmVyIFJpc2sgRmVhdHVyZXMiKSArCiAgcGxvdFRoZW1lKCkKIAogZ2dwbG90KGNvcnJlbGF0aW9uX2xvbmcsIGFlcyh4ID0gY291bnRSb2JiZXJ5KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgZmlsbCA9ICIjYzQ0NTM2IiwgY29sb3IgPSAiIzI4M2QzYiIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiSGlzdG9ncmFtIG9mIFJvYmJlcnkgQ291bnRzIiwKICAgIHggPSAiUm9iYmVyeSBDb3VudHMiLAogICAgeSA9ICJGcmVxdWVuY3kiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCgpgYGAKCiMjIERpc3RhbmNlIHRvIEhvdCBzcG90CgpCZWxvdyBpcyB0aGUgZGlzdGFuY2UgdG8gaG90c3BvdCBwbG90LgpgYGB7ciBkaXN0YW5jZSB0byBob3Qgc3BvdCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmZpbmFsX25ldCA8LSBmaW5hbF9uZXQgJT4lIAogIG11dGF0ZShyb2JiZXJ5LmlzU2lnID0gCiAgICAgICAgICAgaWZlbHNlKGxvY2FsX21vcmFuc1ssNV0gPD0gMC4wMDEsIDEsIDApKSAlPiUKICBtdXRhdGUocm9iYmVyeS5pc1NpZy5kaXN0ID0gCiAgICAgICAgICAgbm5fZnVuY3Rpb24oc3RfYyhzdF9jb2lkKGZpbmFsX25ldCkpLAogICAgICAgICAgICAgICAgICAgICAgIHN0X2Moc3RfY29pZChmaWx0ZXIoZmluYWxfbmV0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9iYmVyeS5pc1NpZyA9PSAxKSkpLCAKICAgICAgICAgICAgICAgICAgICAgICBrID0gMSkpCgpnZ3Bsb3QoKSArCiAgICAgIGdlb21fc2YoZGF0YSA9IGZpbmFsX25ldCwgYWVzKGZpbGw9cm9iYmVyeS5pc1NpZy5kaXN0KSwgY29sb3VyPU5BKSArCiAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lPSJOTiBEaXN0YW5jZSIpICsKICAgICAgbGFicyh0aXRsZT0iUm9iYmVyeSBOTiBEaXN0YW5jZSIpICsKICAgICAgbWFwVGhlbWUoKQpgYGAKCiMjIE1vZGVsaW5nCgojIyMgRm9sZCBhbmQgU3BhdGlhbCBSZWdyZXNzaW9uCgpKdXN0IHJpc2sgZmFjdG9ycyBsaXN0IGNvbnRhaW5zIG9ubHkgc3BhdGlhbCB2YXJpYWJsZXMsIHN1Y2ggYXMgYWJhbmRvbmVkIGNhcnMsIGFiYW5kb25lZCBidWlsZGluZ3MsIHNob3Qgc3BvdHRlciBhbGVydHMsIGFuZCBzdHJlZXQgbGlnaHRzIG91dC4gQmFzZWQgb24gdGhpcywgc3BhdGlhbCBwcm9jZXNzIGxpc3QgY29udGFpbnMgYWRkaXRpb25hbCBjcmltZWhvdHNwb3RzIGxpa2UgYHJvYmJlcnkuaXNTaWdgIGFuZCBgcm9iYmVyeS5pc1NpZy5kaXN0YC4gRmxvZCBhbmQgc3BhdGlhbCByZWdyZXNzaW9ucyBhcmUgZG9uZSB1c2luZyB0aGVzZSB2YXJpYWJsZSBsaXN0cy4KYGBge3IgZm9sZCBhbmQgc3BhdGlhbCByZWdyZXNzaW9uLCByZXN1bHRzID0gJ2hpZGUnfQpyZWcudmFycyA8LSBjKCJBYmFuZG9uZWRfQnVpbGRpbmdzLm5uIiwgIkFiYW5kb25lZF9DYXJzLm5uIiwgIlN0cmVldF9MaWdodHNfT3V0Lm5uIiwgIlRyYWZmaWNfQ3Jhc2gubm4iLCAiU2hvdF9TcG90dGVyX0FsZXJ0cy5ubiIpCgpyZWcuc3MudmFycyA8LSBjKCJBYmFuZG9uZWRfQnVpbGRpbmdzLm5uIiwgIkFiYW5kb25lZF9DYXJzLm5uIiwgIlN0cmVldF9MaWdodHNfT3V0Lm5uIiwgIlRyYWZmaWNfQ3Jhc2gubm4iLCAiU2hvdF9TcG90dGVyX0FsZXJ0cy5ubiIsICJyb2JiZXJ5LmlzU2lnIiwgInJvYmJlcnkuaXNTaWcuZGlzdCIpCgpyZWcuY3YgPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gZmluYWxfbmV0LAogIGlkID0gImN2SUQiLAogIGRlcGVuZGVudFZhcmlhYmxlID0gImNvdW50Um9iYmVyeSIsCiAgaW5kVmFyaWFibGVzID0gcmVnLnZhcnMpICU+JQogIGRwbHlyOjpzZWxlY3QoY3ZJRCA9IGN2SUQsIGNvdW50Um9iYmVyeSwgUHJlZGljdGlvbiwgZ2VvbWV0cnkpCgpyZWcuc3MuY3YgPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gZmluYWxfbmV0LAogIGlkID0gImN2SUQiLAogIGRlcGVuZGVudFZhcmlhYmxlID0gImNvdW50Um9iYmVyeSIsCiAgaW5kVmFyaWFibGVzID0gcmVnLnNzLnZhcnMpICU+JQogIGRwbHlyOjpzZWxlY3QoY3ZJRCA9IGN2SUQsIGNvdW50Um9iYmVyeSwgUHJlZGljdGlvbiwgZ2VvbWV0cnkpCgpyZWcuc3BhdGlhbENWIDwtIGNyb3NzVmFsaWRhdGUoCiAgZGF0YXNldCA9IGZpbmFsX25ldCwKICBpZCA9ICJuYW1lIiwKICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJjb3VudFJvYmJlcnkiLAogIGluZFZhcmlhYmxlcyA9IHJlZy52YXJzKSAlPiUKICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBuYW1lLCBjb3VudFJvYmJlcnksIFByZWRpY3Rpb24sIGdlb21ldHJ5KQoKcmVnLnNzLnNwYXRpYWxDViA8LSBjcm9zc1ZhbGlkYXRlKAogIGRhdGFzZXQgPSBmaW5hbF9uZXQsCiAgaWQgPSAibmFtZSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgZGVwZW5kZW50VmFyaWFibGUgPSAiY291bnRSb2JiZXJ5IiwKICBpbmRWYXJpYWJsZXMgPSByZWcuc3MudmFycykgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBuYW1lLCBjb3VudFJvYmJlcnksIFByZWRpY3Rpb24sIGdlb21ldHJ5KQoKcmVnLnN1bW1hcnkgPC0gCiAgcmJpbmQoCiAgICBtdXRhdGUocmVnLmN2LCBFcnJvciA9IFByZWRpY3Rpb24gLSBjb3VudFJvYmJlcnksCiAgICAgICAgICAgUmVncmVzc2lvbiA9ICJSYW5kb20gay1mb2xkIENWOiBKdXN0IFJpc2sgRmFjdG9ycyIpLAogICAgbXV0YXRlKHJlZy5zcy5jdiwgRXJyb3IgPSBQcmVkaWN0aW9uIC0gY291bnRSb2JiZXJ5LAogICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiUmFuZG9tIGstZm9sZCBDVjogU3BhdGlhbCBQcm9jZXNzIiksCiAgICBtdXRhdGUocmVnLnNwYXRpYWxDViwgRXJyb3IgPSBQcmVkaWN0aW9uIC0gY291bnRSb2JiZXJ5LAogICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiU3BhdGlhbCBMT0dPLUNWOiBKdXN0IFJpc2sgRmFjdG9ycyIpLAogICAgbXV0YXRlKHJlZy5zcy5zcGF0aWFsQ1YsIEVycm9yID0gUHJlZGljdGlvbiAtIGNvdW50Um9iYmVyeSwKICAgICAgICAgICBSZWdyZXNzaW9uID0gIlNwYXRpYWwgTE9HTy1DVjogU3BhdGlhbCBQcm9jZXNzIikKICApICU+JSAKICBzdF9zZigpCgpgYGAKCiMjIyBDYWxjdWxhdGluZyBFcnJvcnMgYWNyb3NzIHNwYWNlCgpUaGUgYWNjdXJhY3kgb2YgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgaXMgbWVhc3VyZWQuIE1lYW4gZXJyb3IsIE1BRSwgYW5kIFNEIE1BRSBhcmUgc2hvd24gaW4gdGhlIG1hcHMsIHBsb3RzLCBhbmQgdGFibGUgYmVsb3cuIEl0IGNhbiBiZSBjb25jbHVkZWQgdGhhdCByYW5kb20gay1mb2xkIG1vZGVsIGZ1bmN0aW9ucyBiZXR0ZXIgdGhhbiBzcGF0aWFsIExPR08gbW9kZWwsIGFuZCBpdCBzZWVtcyB0aGF0IGluIHRoZSByYW5kb20gay1mb2xkIG1vZGVsLCBzcGF0aWFsIHByb2Nlc3MgbW9kZWwgd29ya3MgbW9yZSBhY2N1cmF0ZSB0aGFuIGp1c3QgcmlzayBmYWN0b3JzIG1vZGVsLgpgYGB7ciBlcnJvciBhY3Jvc3Mgc3BhY2UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmVycm9yX2J5X3JlZ19hbmRfZm9sZCA8LSAKICByZWcuc3VtbWFyeSAlPiUKICAgIGdyb3VwX2J5KGN2SUQsIFJlZ3Jlc3Npb24pICU+JSAKICAgIHN1bW1hcml6ZShNZWFuX0Vycm9yID0gbWVhbihQcmVkaWN0aW9uIC0gY291bnRSb2JiZXJ5LCBuYS5ybSA9IFQpLAogICAgICAgICAgICAgIE1BRSA9IG1lYW4oYWJzKE1lYW5fRXJyb3IpLCBuYS5ybSA9IFQpLAogICAgICAgICAgICAgIFNEX01BRSA9IG1lYW4oYWJzKE1lYW5fRXJyb3IpLCBuYS5ybSA9IFQpKSAlPiUKICAgICB1bmdyb3VwKCkKCgp2YXJzIDwtIHVuaXF1ZShlcnJvcl9ieV9yZWdfYW5kX2ZvbGQkUmVncmVzc2lvbikKbWFwTGlzdCA8LSBsaXN0KCkKCmZvcihpIGluIHZhcnMpewogIG1hcExpc3RbW2ldXSA8LSAKICAgIGdncGxvdCgpICsKICAgIGdlb21fc2YoZGF0YSA9IGZpbHRlcihlcnJvcl9ieV9yZWdfYW5kX2ZvbGQsIFJlZ3Jlc3Npb24gPT0gaSksIGFlcyhmaWxsPU1BRSksIGNvbG91cj1OQSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWU9IiIpICsKICAgIGxhYnModGl0bGU9aSwgc2l6ZSA9IDEwKSArCiAgICBtYXBUaGVtZSgpfQoKZG8uY2FsbChncmlkLmFycmFuZ2UsYyhtYXBMaXN0LCBuY29sID0gMiwgdG9wID0gIk1BRSBieSBGb2xkIGFuZCBSZWdyZXNzaW9uIikpCgplcnJvcl9ieV9yZWdfYW5kX2ZvbGQgJT4lCiAgZ2dwbG90KGFlcyhNQUUpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCwgY29sb3VyPSIjMjgzZDNiIiwgZmlsbCA9ICIjYzQ0NTM2IikgKwogIGZhY2V0X3dyYXAoflJlZ3Jlc3Npb24pICsgIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDApICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA4LCBieSA9IDEpKSArIAogIGxhYnModGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiBNQUUiLCBzdWJ0aXRsZSA9ICJrLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiB2cy4gTE9HTy1DViIsCiAgICAgICB4PSJNZWFuIEFic29sdXRlIEVycm9yIiwgeT0iQ291bnQiKSArCiAgcGxvdFRoZW1lKCkKCiBlcnJvcl9ieV9yZWdfYW5kX2ZvbGQgPC0gCiAgcmVnLnN1bW1hcnkgJT4lCiAgICBncm91cF9ieShSZWdyZXNzaW9uKSAlPiUgCiAgICBzdW1tYXJpemUoTWVhbl9FcnJvciA9IG1lYW4oUHJlZGljdGlvbiAtIGNvdW50Um9iYmVyeSwgbmEucm0gPSBUKSwKICAgICAgICAgICAgICBNQUUgPSBtZWFuKGFicyhNZWFuX0Vycm9yKSwgbmEucm0gPSBUKSwKICAgICAgICAgICAgICBTRF9NQUUgPSBtZWFuKGFicyhNZWFuX0Vycm9yKSwgbmEucm0gPSBUKSkgJT4lCiAgICAgdW5ncm91cCgpCiAKIGVycm9yX2J5X3JlZ19hbmRfZm9sZCAlPiUgCiBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAga2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSkgICU+JSAKICByb3dfc3BlYygyLCBjb2xvciA9ICIjMjgzZDNiIiwgYmFja2dyb3VuZCA9ICIjZTllY2VmIikgJT4lCiAgcm93X3NwZWMoNCwgY29sb3IgPSAiIzI4M2QzYiIsIGJhY2tncm91bmQgPSAiI2U5ZWNlZiIpIAogCmBgYAoKIyMgRXJyb3JzIGJ5IFJhY2UKClRoZSByYXcgZXJyb3JzIG9mIG1vZGVsIGluIHJhY2UgdHlwZSBhcmUgc2hvd24gaW4gdGhlIGZvbGxvd2luZyB0YWJsZS4gUmFuZG9tIGstZm9sZCBtb2RlbCBzdGlsbCBwZXJmb3JtcyBiZXR0ZXIgdGhhbiBzcGF0aWFsIExPR08gbW9kZWwgaW4gYm90aCBtYWpvcml0eSB3aGl0ZSBhbmQgbWFqb3JpdHkgbm9uLXdoaXRlIHJlZ2lvbnMuIEhvd2V2ZXIsIHRoZSBlcnJvciBvZiBzcGF0aWFsIHByb2Nlc3MgbW9kZWwgaXMgdmVyeSBjbG9zZSB0byBqdXN0IHJpc2sgZmFjdG9ycyBtb2RlbCBpbiB0aGUgY29tcGFyaXNvbi4KCkFsbCB0aGUgbW9kZWxzIHNob3cgZGlmZmVyZW5jZSBiZXR3ZWVuIG1ham9yaXR5IHdoaXRlIGFuZCBtYWpvcml0eSBub24td2hpdCBpbiBlcnJvci4gQ29tcGFyZWQgdG8gbW9kZWwgd2l0aCBqdXN0IHJpc2sgZmFjdG9ycywgdGhlIGVycm9ycyBvZiB0d28gcmVnaW9ucyBhcmUgcmVsZXZhbnRseSBjbG9zZSB0byBlYWNoIG90aGVyIGluIG1vZGVsIHdpdGggc3BhdGlhbCBwcm9jZXNzLCB3aGljaCBtYXkgaW5kaWNhdGUgYSBiZXR0ZXIgZ2VuZXJhbGl6YWJpbGl0eS4KYGBge3IgZ2V0IGFjcywgaW5jbHVkZT1GQUxTRX0KY2Vuc3VzX2FwaV9rZXkoImVlNWJiMzAzZDU2MmExMzRhNWZjMmNkYmM0YWI1M2M4ZDBjYTc2MjkiLCBvdmVyd3JpdGUgPSBUUlVFKQoKdHJhY3RzMTcgPC0gCiAgZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCAKICAgICAgICAgIHZhcmlhYmxlcyA9IGMoIkIwMjAwMV8wMDFFIiwgIyB0b3RhbCBwb3B1bGF0aW9uCiAgICAgICAgICAgICJCMDIwMDFfMDAyRSIgKSwgICMgd2hpdGUgcG9wdWxhdGlvbgogICAgICAgICAgeWVhcj0yMDE3LCBzdGF0ZT0xNywgY291bnR5PTMxLCAKICAgICAgICAgIGdlb21ldHJ5PVRSVUUsIG91dHB1dD0id2lkZSIpICU+JQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDIyNzEnKSAlPiUgCiAgcmVuYW1lKFRvdGFsUG9wID0gQjAyMDAxXzAwMUUsIAogICAgICAgICBXaGl0ZSA9IEIwMjAwMV8wMDJFKSAlPiUgCiAgbXV0YXRlKHBjdFdoaXRlID0gV2hpdGUvVG90YWxQb3AgLCAKICAgICAgICAgUmFjZSA9IGlmZWxzZShwY3RXaGl0ZSA+IDAuNSwgIk1ham9yaXR5V2hpdGUiLCAiTWFqb3JpdHlOb25XaGl0ZSIpKSAlPiUgCiAgICAuW25laWdoYm9yaG9vZHMsXQpgYGAKCmBgYHtyIGVycm9yIGJ5IHJhY2UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpyZWcuc3VtbWFyeSAlPiUgCiAgc3RfY2VudHJvaWQoKSAlPiUgCiAgc3Rfam9pbih0cmFjdHMxNykgJT4lCiAgbmEub21pdCgpICU+JQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICBncm91cF9ieShSZWdyZXNzaW9uLCBSYWNlKSAlPiUKICBzdW1tYXJpemUobWVhbi5FcnJvciA9IG1lYW4oRXJyb3IsIG5hLnJtID0gVCkpICU+JQogIHNwcmVhZChSYWNlLCBtZWFuLkVycm9yKSAlPiUKICBrYWJsZShjYXB0aW9uID0gIk1lYW4gRXJyb3IgYnkgbmVpZ2hib3Job29kIHJhY2lhbCBjb250ZXh0IikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikpICAlPiUgCiAgcm93X3NwZWMoMiwgY29sb3IgPSAiIzI4M2QzYiIsIGJhY2tncm91bmQgPSAiI2U5ZWNlZiIpICU+JQogIHJvd19zcGVjKDQsIGNvbG9yID0gIiMyODNkM2IiLCBiYWNrZ3JvdW5kID0gIiNlOWVjZWYiKSAKCmBgYAoKIyBEZW5zaXR5IHZzIHByZWRpY3Rpb25zCgpUaGUgZm9sbG93aW5nIHBsb3QgaXMgdGhlIGtlcm5lbCBkZW5zaXR5IG9mIHJvYmJlcmllcyBpbiAyMDE3LiBUaGUgcmVnaW9ucyB3aXRoIGhpZ2gga2VybmVsIGRlbnNpdHkgaGF2ZSBhIGhpZ2hlciBwcm9iYWJpbGl0eSBvZiByb2JiZXJpZXMgaW4gdGhlIG5leHQgeWVhci4KYGBge3IgMjAxNyByb2JiZXJ5LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyb2JfcHBwIDwtIGFzLnBwcChzdF9jb29yZGluYXRlcyhyb2JiZXJ5KSwgVyA9IHN0X2Jib3goZmluYWxfbmV0KSkKcm9iX0tELjEwMDAgPC0gc3BhdHN0YXQuZXhwbG9yZTo6ZGVuc2l0eS5wcHAocm9iX3BwcCwgMTAwMCkKCnJvYl9LRC5kZiA8LSByYmluZCgKICBtdXRhdGUoZGF0YS5mcmFtZShyYXN0ZXJUb1BvaW50cyhtYXNrKHJhc3Rlcihyb2JfS0QuMTAwMCksIGFzKG5laWdoYm9yaG9vZHMsICdTcGF0aWFsJykpKSksIExlZ2VuZCA9ICIxMDAwIEZ0LiIpKSAKCnJvYl9LRC5kZiRMZWdlbmQgPC0gZmFjdG9yKHJvYl9LRC5kZiRMZWdlbmQsIGxldmVscyA9IGMoIjEwMDAgRnQuIikpCgphcy5kYXRhLmZyYW1lKHJvYl9LRC4xMDAwKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKGZpbmFsX25ldCkpICU+JQogIGFnZ3JlZ2F0ZSguLCBmaW5hbF9uZXQsIG1lYW4pICU+JQogICBnZ3Bsb3QoKSArCiAgICAgZ2VvbV9zZihhZXMoZmlsbD12YWx1ZSkpICsKICAgICBnZW9tX3NmKGRhdGEgPSBzYW1wbGVfbihyb2JiZXJ5LCAxNTAwKSwgc2l6ZSA9IC41LCBjb2xvciA9ICIjMjgzZDNiIikgKwogICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lID0gIkRlbnNpdHkiKSArCiAgICAgbGFicyh0aXRsZSA9ICJLZXJuZWwgRGVuc2l0eSBvZiAyMDIwIFJvYmJlcnkiKSArCiAgICAgbWFwVGhlbWUodGl0bGVfc2l6ZSA9IDE0KQoKYGBgCkNvbXBhcmVkIHRvIHByZWRpY3Rpb24gbWFkZSBieSBrZXJuZWwgZGVuc2l0eSBhbmFseXNpcywgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgbWFrZXMgYSBtb3JlIGFjY3VyYXRlIHByZWRpY3Rpb24gb24gdGhlIGdlb2dyYXBoaWMgbGV2ZWwuIFJlZ3Jlc3Npb24gbW9kZWwgd29ya3MgbW9yZSBwcmVjaXNlbHkgaW4gbW9zdCBwYXJ0cyBvZiB0aGUgY2l0eSwgZXNwZWNpYWxseSB0aGUgc291dGhlcm4gcGFydCwgYnV0IHNlZW1zIHRvIHVuZGVyZXN0aW1hdGUgdGhlIHJvYmJlcnkgcmFuayBpbiBub3J0aCBDaGljYWdvIGF0IHRoZSBzYW1lIHRpbWUuIE1lYW53aGlsZSwgdGhlIHJlc3VsdCBvZiBrZXJuZWwgZGVuc2l0eSBhbmFseXNpcyBpcyBtb3JlIHJlbGlhYmxlIGluIG5vcnRoZWFzdCBhcmVhLCB3aGVyZSByZWdyZXNzaW9uIG1vZGVsIGZhaWxzIHRvIHJhbmsgYSBoaWdoZXIgbGV2ZWwgb2Ygcm9iYmVyeSBwb3NzaWJpbGl0eS4KYGBge3IgS0QgYW5kIFByZWRpY3Rpb24sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJvYmJlcnkyMSA8LSAKICByZWFkLnNvY3JhdGEoImh0dHBzOi8vZGF0YS5jaXR5b2ZjaGljYWdvLm9yZy9QdWJsaWMtU2FmZXR5L0NyaW1lcy0yMDIxL2R3bWUtdDk2YyIpICU+JSAKICBmaWx0ZXIoUHJpbWFyeS5UeXBlID09ICJST0JCRVJZIiAmIERlc2NyaXB0aW9uID09ICJBUk1FRCAtIEhBTkRHVU4iKSAlPiUKICBtdXRhdGUoeCA9IGdzdWIoIlsoKV0iLCAiIiwgTG9jYXRpb24pKSAlPiUKICBzZXBhcmF0ZSh4LGludG89IGMoIlkiLCJYIiksIHNlcD0iLCIpICU+JQogIG11dGF0ZShYID0gYXMubnVtZXJpYyhYKSwKICAgICAgICAgWSA9IGFzLm51bWVyaWMoWSkpICU+JSAKICBuYS5vbWl0ICU+JQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoIlgiLCAiWSIpLCBjcnMgPSA0MzI2LCBhZ3IgPSAiY29uc3RhbnQiKSAlPiUKICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyMjcxJykgJT4lIAogIGRpc3RpbmN0KCkgJT4lCiAgLltmaXNobmV0LF0KCnJvYl9LREVfc3VtIDwtIGFzLmRhdGEuZnJhbWUocm9iX0tELjEwMDApICU+JQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoIngiLCAieSIpLCBjcnMgPSBzdF9jcnMoZmluYWxfbmV0KSkgJT4lCiAgYWdncmVnYXRlKC4sIGZpbmFsX25ldCwgbWVhbikgCmtkZV9icmVha3MgPC0gY2xhc3NJbnRlcnZhbHMocm9iX0tERV9zdW0kdmFsdWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSA1LCAiZmlzaGVyIikKcm9iX0tERV9zZiA8LSByb2JfS0RFX3N1bSAlPiUKICBtdXRhdGUobGFiZWwgPSAiS2VybmVsIERlbnNpdHkiLAogICAgICAgICBSaXNrX0NhdGVnb3J5ID0gY2xhc3NJbnQ6OmZpbmRDb2xzKGtkZV9icmVha3MpLAogICAgICAgICBSaXNrX0NhdGVnb3J5ID0gY2FzZV93aGVuKAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gNSB+ICI1dGgiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gNCB+ICI0dGgiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gMyB+ICIzcmQiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gMiB+ICIybmQiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gMSB+ICIxc3QiKSkgJT4lCiAgY2JpbmQoCiAgICBhZ2dyZWdhdGUoCiAgICAgIGRwbHlyOjpzZWxlY3Qocm9iYmVyeTIxKSAlPiUgbXV0YXRlKHJvYmJlcnlDb3VudCA9IDEpLCAuLCBzdW0pICU+JQogICAgbXV0YXRlKHJvYmJlcnlDb3VudCA9IHJlcGxhY2VfbmEocm9iYmVyeUNvdW50LCAwKSkpICU+JQogIGRwbHlyOjpzZWxlY3QobGFiZWwsIFJpc2tfQ2F0ZWdvcnksIHJvYmJlcnlDb3VudCkKCgptbF9icmVha3MgPC0gY2xhc3NJbnRlcnZhbHMocmVnLnNzLnNwYXRpYWxDViRQcmVkaWN0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gNSwgImZpc2hlciIpCnJvYl9yaXNrX3NmIDwtCiAgcmVnLnNzLnNwYXRpYWxDViAlPiUKICBtdXRhdGUobGFiZWwgPSAiUmlzayBQcmVkaWN0aW9ucyIsCiAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPWNsYXNzSW50OjpmaW5kQ29scyhtbF9icmVha3MpLAogICAgICAgICBSaXNrX0NhdGVnb3J5ID0gY2FzZV93aGVuKAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gNSB+ICI1dGgiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gNCB+ICI0dGgiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gMyB+ICIzcmQiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gMiB+ICIybmQiLAogICAgICAgICAgIFJpc2tfQ2F0ZWdvcnkgPT0gMSB+ICIxc3QiKSkgJT4lCiAgY2JpbmQoCiAgICBhZ2dyZWdhdGUoIAogICAgICBkcGx5cjo6c2VsZWN0KHJvYmJlcnkyMSkgJT4lIG11dGF0ZShyb2JiZXJ5Q291bnQgPSAxKSwgLiwgc3VtKSAlPiUKICAgICAgbXV0YXRlKHJvYmJlcnlDb3VudCA9IHJlcGxhY2VfbmEocm9iYmVyeUNvdW50LCAwKSkpICU+JQogIGRwbHlyOjpzZWxlY3QobGFiZWwsUmlza19DYXRlZ29yeSwgcm9iYmVyeUNvdW50KQoKCnJiaW5kKHJvYl9LREVfc2YsIHJvYl9yaXNrX3NmKSAlPiUKICBuYS5vbWl0KCkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWxhYmVsLCAtUmlza19DYXRlZ29yeSwgLWdlb21ldHJ5KSAlPiUKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3NmKGFlcyhmaWxsID0gUmlza19DYXRlZ29yeSksIGNvbG91ciA9IE5BKSArCiAgICBnZW9tX3NmKGRhdGEgPSBzYW1wbGVfbihyb2JiZXJ5MjEsIDMwMDAsIHJlcGxhY2UgPSBUUlVFKSwgc2l6ZSA9IC41LCBjb2xvdXIgPSAiIzI4M2QzYiIpICsKICAgIGZhY2V0X3dyYXAofmxhYmVsLCApICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZSA9IFRSVUUpICsKICAgIGxhYnModGl0bGU9IkNvbXBhcmlzb24gb2YgS2VybmVsIERlbnNpdHkgYW5kIFJpc2sgUHJlZGljdGlvbnMiLAogICAgICAgICBzdWJ0aXRsZT0iMjAyMCByb2JiZXJ5IHJpc2sgcHJlZGljdGlvbnM7IDIwMjEgcm9iYmVyeSIpICsKICAgIG1hcFRoZW1lKHRpdGxlX3NpemUgPSAxNCkKYGBgCkFzIGNhbiBiZSBzZWVuIGluIHRoZSBjaGFydCBiZWxvdywgdGhlcmUncyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmVzdWx0IHByb2R1Y2VkIGJ5IGtlcm5lbCBkZW5zaXR5IGFuYWx5c2lzIGFuZCB0aGUgcHJlZGljdGlvbiBtYWRlIHJlZ3Jlc3Npb24gbW9kZWwuIEtlcm5lbCBkZW5zaXR5IGFuYWx5c2lzIHByZWRpY3RzIG1vcmUgY2FzZXMgaW4gaGlnaCByYW5rIGdyb3VwcyAoM3JkLCA0dGggYW5kIDV0aCksIGVzcGVjaWFsbHkgaW4gdGhlIDR0aCByYW5rLCB3aGlsZSByZWdyZXNzaW9uIG1vZGVsIHByZWRpY3RzIG1vcmUgY2FzZXMgaW4gbG93IHJhbmsgZ3JvdXBzICgxc3QgYW5kIDJuZCksIGVzcGVjaWFsbHkgaW4gdGhlIDFzdCByYW5rLiBJbiBnZW5lcmFsLCBrZXJuZWwgZGVuc2l0eSBhbmFseXNpcyBwcmVkaWN0cyBhIGluY3JlYXNlIHRyZW5kIGluIHJvYmJlcnkgY2FzZXMgYW5kIGNvdmVyYWdlLCBhcyByZWdyZXNzaW9uIG1vZGVsIHN1Z2dlc3RzIHRoZXJlIHNob3VsZCBiZSBhIGRlY2xpbmVkIHRyZW5kIGluIDIwMjEgb24gdGhlIGNvbnRyYXJ5LgpgYGB7ciBoaXN0Z3JhbSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmJpbmQocm9iX0tERV9zZiwgcm9iX3Jpc2tfc2YpICU+JQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICBuYS5vbWl0KCkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWxhYmVsLCAtUmlza19DYXRlZ29yeSkgJT4lCiAgZ3JvdXBfYnkobGFiZWwsIFJpc2tfQ2F0ZWdvcnkpICU+JQogIHN1bW1hcml6ZShjb3VudFJvYmJlcnkgPSBzdW0oVmFsdWUpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkobGFiZWwpICU+JQogIG11dGF0ZShQY250X29mX3Rlc3Rfc2V0X2NyaW1lcyA9IGNvdW50Um9iYmVyeSAvIHN1bShjb3VudFJvYmJlcnkpKSAlPiUKICAgIGdncGxvdChhZXMoUmlza19DYXRlZ29yeSxQY250X29mX3Rlc3Rfc2V0X2NyaW1lcykpICsKICAgICAgZ2VvbV9iYXIoYWVzKGZpbGw9bGFiZWwpLCBwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsKICAgICAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlID0gVFJVRSwgbmFtZSA9ICJNb2RlbCIpICsKICAgICAgbGFicyh0aXRsZSA9ICJSaXNrIHByZWRpY3Rpb24gdnMuIEtlcm5lbCBkZW5zaXR5LCAyMDIxIHJvYmJlcnkiLAogICAgICAgICAgIHkgPSAiJSBvZiBUZXN0IFNldCBSb2JiZXJ5IChwZXIgbW9kZWwpIiwKICAgICAgICAgICB4ID0gIlJpc2sgQ2F0ZWdvcnkiKSArCiAgdGhlbWVfYncoKSArCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMC41KSkKYGBgCgojIENvbmNsdXNpb24KCkluIHRoaXMgcHJvamVjdCwgZ2Vvc3BhdGlhbCBmZWF0dXJlcyBhcmUgZXhhbWluZWQgYW5kIGluY2x1ZGVkIGluIHRoZSByaXNrIHJlZ3Jlc3Npb24gbW9kZWwgdG8gcmV2ZWFsIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzcGF0aWFsIGZlYXR1cmVzIGFuZCB0aGUgY3JpbWUgYXR0ZW1wdHMsIGVzcGVjaWFsbHkgcm9iYmVyeSwgaW4gQ2hpY2Fnby4gQWx0aG91Z2ggdGhlIHByZWRpY3Rpb25zIG1hZGUgYnkgdGhlIG1vZGVsIG1heSBub3QgYmUgcHJlY2lzZSBlbm91Z2ggaW4gc29tZSBwYXJ0aWN1bGFyIGFyZWFzLCB0aGlzIHByb2plY3Rpb24gY2FuIGV4cGxhaW4gdGhlIGN1cnJlbnQgY3JpbWUgcGF0dGVybiBpbiBDaGljYWdvIHRvIHNvbWUgZXh0ZW50LiAKCkhvd2V2ZXIsIEkgd2lsbCBwcm9iYWJseSBub3QgcmVjb21tZW5kIHRoZSByZWdyZXNzaW9uIG1vZGVsIGZvciBwcm9kdWN0aW9uIHVzZS4gRmlyc3RseSwgdGFraW5nIHRoZSBmdXR1cmUgcHJlZGljdGlvbiBhcyBhbiBleGFtcGxlLCB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgaXMgbm90IGlkZWFsIGluIG5vcnRoZXJuIHBhcnQgb2YgQ2hpY2FnbzogY29tcGFyZWQgdG8ga2VybmVsIGRlbnNpdHkgYW5hbHlzaXMsIGl0IG5vdGFibHkgdW5kZXJlc3RpbWF0ZXMgcm9iYmVyaWVzIGluIHRoZSBub3J0aGVhc3QuIE1vcmVvdmVyLCB0aGUgTUFFIGFuZCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIE1BRSBhcmUgdG9vIHNtYWxsLCB3aGljaCBtYXkgaW5kaWNhdGUgdGhhdCB0aGUgcmVncmVzc2lvbiBtb2RlbCBpcyBvdmVyZml0dGluZy4gVGhpcyB3aWxsIHJlc3VsdCBpbiB0aGUgZGVjcmVhc2UgaW4gZ2VuZXJhbGl6YWJpbGl0eSwgYW5kIHRodXMgcHJvZHVjaW5nIG1vcmUgZXJyb3JzIGluIHByZWRpY3Rpb24uIEFkZGl0aW9uYWxseSwgdGhlIHNlbGVjdGVkIGluZGVwZW5kZW50IHZhcmlhYmxlcyBjYW4gb25seSBwYXJ0bHkgcmVwcmVzZW50IHNvbWUgZmFjdG9ycyBkcml2aW5nIHJvYmJlcmllcyBpbiBDaGljYWdvLiBPdGhlciBlY29ub21pYyBhbmQgZGVtb2dyYXBoaWMgZmFjdG9ycyBhcmUgZGlzbWlzc2VkIGluIHRoaXMgbW9kZWwsIGxlYWRpbmcgdG8gYW4gaW5jb21wcmVoZW5zaXZlIGludGVycHJldGF0aW9uIG9mIHJvYmJlcmllcy4gRnVydGhlcm1vcmUsIHRoZSBiYXNlIGRhdGFzZXQgaXMgb25seSBhYm91dCAyMDE3LCB3aGljaCBpcyBub3QgcmVwcmVzZW50YXRpdmUgZW5vdWdoLCBhbmQgbW9yZSBkYXRhIGFib3V0IG90aGVyIHllYXJzIHNob3VsZCBiZSB0YWtlbiBpbnRvIGNvbnNpZGVyYXRpb24gdG8gcHJvZHVjZSBhIG1vcmUgcmVsaWFibGUgbW9kZWwuIAoKRHVlIHRvIHRoZXNlIHJlYXNvbnMsIEkgd291bGQgbGVzcyBsaWtlbHkgdG8gcmVjb21tZW5kIHRoaXMgcmVncmVzc2lvbiBtb2RlbCBmb3IgcHJvZHVjdGlvbiB1c2UsIGJ1dCBJIHdpbGwgc3VnZ2VzdCB0aGF0IHRoZSBtb2RlbCBjYW4gYmUgY29uc2lkZXJlZCBhcyBhIHJlZmVyZW5jZSBpbiBmaXR0aW5nIHN1aXRhYmxlIG1vZGVsIGZvciBwb2xpY2UgcHJhY3RpY2UsIHNpbmNlIHNvbWUgdmFyaWFibGVzIGluIHRoZSByZWdyZXNzaW9uIG1vZGVsIHNob3cgc2lnbmlmaWNhbnQgcmVsYXRpb25zaGlwcyB3aXRoIHJvYmJlcmllcyBhbmQgdGhlIG1vZGVsIG1ha2VzIGEgd2VsbCBwcmVkaWN0aW9uIGluIHNvbWUgcGFydHMgb2YgQ2hpY2FnbyBpbiBwcmVkaWN0aW9uLgo=